不一样的面向对象(三)

用户头像
书旅
关注
发布于: 2020 年 09 月 29 日
不一样的面向对象(三)

设计模式六大原则



里式替换原则(LSP)

定义:所有引用基类的地方都必须能透明地使用其子类进行替换(简单说就是:子类可以扩展基类的功能,但是不能改变基类原有的功能)



里式替换原则是继承复用的基石,只有当子类可以替换掉基类,且其它功能不受到影响,基类才算真正的能够被复用,子类可以在基类的基础上增加新的方法



里式替换原则核心就是继承,通过继承,引用基类的地方就可以使用子类的对象了



继承的优点

(1)代码共享,每个子类都拥有父类的方法和属性



(2)提高了代码的重用性



(3)子类可以在父类的基础上扩展自己特有的功能



继承的缺点

(1)继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法



(2)降低代码的灵活性。子类必须拥有父类的属性和方法



(3)增强了耦合性。当父类的常量、变量和方法被修改时,必需要考虑子类的修改



使用里式替换原则注意的点

(1)子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法



(2)子类中可以增加自己特有的方法



(3)当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松



(4)当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格



1、子类必须实现父类的抽象方法,但不得重写(覆盖)父类的非抽象(已实现)方法

<?php
abstract class BaseClass
{
public abstract function abstractAction();
public function notAbstractAction() {
echo "我是基类中的非抽象方法".PHP_EOL;
}
}
class SonClass extends BaseClass {
public function abstractAction()
{
echo "我是子类,我实现了父类的抽象方法";
}
public function notAbstractAction()
{
echo "我是子类,我重写了基类的非抽象方法";
}
}
class SonClass2 extends BaseClass {
public function abstractAction()
{
echo "我是子类2,我实现了父类的抽象方法";
}
public function mySelfAction() {
echo "我是子类2,这是我特有的方法";
}
}
//如果将参数由基类替换成子类,该方法输出的结果就会改变
function testFunction(BaseClass $obj) {
$obj->notAbstractAction();
}
testFunction(new SonClass2());
testFunction(new SonClass());
输出:
我是基类中的非抽象方法
我是子类,我重写了基类的非抽象方



在上边的例子里边,子类SonClass重写了基类中的非抽象发方法notAbstractAction(),如果将参数中的基类替换成SonClass,就会导致打印的结果发生改变。这样就会导致在父类出现的地方,不能由子类完全替换,违背了“里氏替换原则”



2、子类中可以增加自己特有的方法



还是借用上边的那个例子,SonClass2类中实现了自己特有的方法mySelfAction,子类可以拥有自己特有的方法,去实现其他的业务逻辑



依赖倒置原则(DIP)

定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,其核心思想是:要面向接口编程,不要面向实现编程



通俗来说就是:抽象不应该依赖于细节,细节应当依赖于抽象。 换言之,要针对接口编程,而不是针对实现编程



因为细节很容易发生改变,不稳定。而抽象通常是一个规范,不经常改变



依赖倒置原则的实现方法



  • 每个类尽量提供接口或抽象类

  • 变量的声明类型应该尽量是接口或者是抽象类

  • 任何类都不应该从具体类派生

  • 使用继承的时候,要尽量遵循上边提到的里式替换原则



代码示例

<?php
//射手类
class Shooter
{
//每一个射手都能够进行射击
public function shot(Baili $obj) {
$obj->shot();
}
}
class Baili
{
public function shot() {
echo "百里开始射击了".PHP_EOL;
}
}
class Mengya
{
public function shot() {
echo "蒙犽开始射击了".PHP_EOL;
}
}
$shooter = new Shooter();
$shooter->shot(new Baili());



上边的类,正常的功能实现了,但是,因为射手类的射击(shot)这个功能是基于具体的类百里类(Baili)实现的,这就给以后的扩展带来了麻烦。这个时候有一个新的射手蒙犽,但是根据射手类的射击方法,蒙犽没法进行射击,除非对射手类的射击方法进行修改。按理说,只要是射手属性的英雄,就应该能够进行射击



改进,射手类中的射击方法,不应该依赖具体的某一个射手英雄,而应该是射手的泛指

<?php
//射手类
class Shooter implements BaseShooter
{
//每一个射手都能够进行射击
public function shot(BaseShooter $obj) {
$obj->attack();
}
public function accack()
{
}
}
interface BaseShooter {
public function shot(BaseShooter $shooter);
public function accack();
}
class Baili extends Shooter
{
public function attack() {
echo "百里开始射击了".PHP_EOL;
}
}
class Mengya extends Shooter
{
public function attack() {
echo "蒙犽开始射击了".PHP_EOL;
}
}
$shooter = new Shooter();
$shooter->shot(new Baili());



这样再进行扩展的话,就会相对的容易一些,耦合度没有上边那种方式大





发布于: 2020 年 09 月 29 日 阅读数: 17
用户头像

书旅

关注

公众号:IT猿圈 2019.04.11 加入

还未添加个人简介

评论

发布
暂无评论
不一样的面向对象(三)