面向对象6大原则(请简述面向对象的几个原则)
面向对象封装的三个特点,继承和多态什么是封装?
客观事物封装成抽象类,类只能允许可信类或对象操作自己的数据和方法,隐藏不可信类的信息。简单来说,封装把对象的设计者和对象的用户分开,用户只需要知道对象能做什么,不需要知道如何实现。封装可以帮助提高类和系统的安全性。
什么是继承?
继承是指建立一个新的派生类,继承一个或多个以前定义的类的数据和函数,可以重新定义或添加新的数据和函数,从而建立类的层次结构或级别。
什么是多态性?
多态是指不同类的同一个操作和实例会产生不同的执行结果,即不同类的对象在收到相同的消息时会得到不同的结果。
代码示例类eat { 公共活动早餐 { Echo吃早饭吧!; } }
班级打字员 { 公共函数类型 { 回声打字!; }
}
函数打印工作($obj) { if ($obj instanceof eat) { echo $ obj-早餐; } elseif ($obj instanceof打字员){ echo $ obj-type; } else { 回声误差; } }
printWorking(new eat); echo PHP _ EOL printWorking(新打字员);输出:
吃早饭!打字!
五个面向对象原则:单一责任原则、开放与封闭原则、Richter替换原则、依赖反转原则和接口隔离原则
单一责任的原则是什么?
简单来说,一个类只做一件事,不要为该类实现太多的功能点,避免同样的职责分散到不同的类。如果一个类的责任太多,那么修改的理由就多了,代码就更耦合了。如果职责不明确,会使代码难以维护。
例如,工厂模式被称为工厂模式,因为它负责“生产”对象。
代码示例工厂模式的优点:一个对象在很多地方都是新创建的。当类名改变时,不需要逐个修改,只需要修改一个地方。
& lt?服务器端编程语言(Professional Hypertext Preprocessor的缩写) 类别MyObject { public function __construct { 回声测试代码; } }
//工厂类 类别MyFactory { 公共静态函数工厂 { 返回新的MyObject; } }
$ instance = MyFactory::factory;//测试代码什么是开闭原理?
开闭原则:类是可扩展的,但不可修改。也就是说,对扩展开放,对修改封闭。
1.向扩展开放意味着当有新的需求或变化时,可以扩展现有的代码以适应新的情况。
2.修改关闭,扩展模块功能时不要影响已有程序模块。
实现开合原理的关键点:抽象编程,不是具体编程。因为抽象相对稳定,类依赖于固定的抽象类和接口,所以修改是封闭的。面向对象的继承和多态机制可以继承抽象类或实现接口,它是开放的,因为它听说过重写它的方法来改变固有的行为和实现方法的新扩展。
比如Decorator模式可以动态添加修改类的功能。类提供了一个函数。如果你想修改和添加额外的函数,在传统的编程模式下,你需要编写一个子类来继承它,并重新实现类的方法。使用装饰器模式,您只需要在运行时添加一个装饰器对象来实现最大的灵活性。
& lt?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
/** *输出字符串 *装饰器动态添加功能 *类回声文本 */ 类回声文本 { 受保护的$ decorator =[];
公共函数索引 { //调用decorator的前置操作 $ this-BeforeEcho; 你好,艾可,我是一名室内设计师。; //调用装饰器的后期操作 $ this-after echo; }
//添加装饰器 公共函数AddDecorator(Decorator $ Decorator) { $ this-decorator[]= $ decorator; }
//对裱花机的预操作实行先进先出的原则 受保护的函数beforeEcho { foreach($ this-decorator as $ decorator) $ decorator-before; }
//实行先进后出的原则,装修后后方作业 受保护的函数afterEcho { $ tmp = array _ reverse($ this-decorator); foreach ($tmp as $decorator) $ decorator-after; } }
/** *装饰界面 *类装饰器 */ 界面装饰器 { 公共函数before;
后的公共函数; }
/** *色彩装饰器实现 * Class ColorDecorator */ 类ColorDecorator实现Decorator { 受保护的$ color
public function _ _ construct($ color) { $ this-color = $ color; }
之前的公共函数 { echo <。dis style = & # 39颜色:{ $ this-color } & # 39;; }
后的公共函数 { echo <。/div; } }
/** *字体大小装饰器实现 *类别大小装饰器 */ 类别大小装饰器实现装饰器 { 受保护的$ size
public function _ _ construct($ size) { $ this-size = $ size; }
之前的公共函数 { echo <。dis style = & # 39font-size:{ $ this-size } px & # 39;**; }
后的公共函数 { echo <。/div; } }
//实例化输出类 $ echo = new echo text; //添加装饰器 $echo-addDecorator(新ColorDecorator(& # 39;红色& # 39;)); //添加装饰器 $echo-addDecorator(新SizeDecorator(& # 39;22')); //输出 $ echo-Index;里希特替换原理是什么?
由于面向对象编程技术中的继承在具体编程中过于简单,所以在很多系统的设计和编程实现中,我们并没有认真理性地考虑应用系统中各个类之间的继承关系是否合适,派生类能否正确重写基类中的一些方法。所以经常出现滥用继承或者错误继承的情况,给系统后期维护带来很多麻烦。
核心思想:子类必须能够替换它们的父类。这个思想体现在继承机制的约束规范中。只有子类可以代替父类,系统才能在运行时识别子类,这是继承复用的基础。
& lt?服务器端编程语言(Professional Hypertext Preprocessor的缩写) //示例1 鸟级{ 保护功能fly{
} } //翠鸟 翠鸟级延伸鸟{
}
//鸵鸟 鸵鸟级延伸鸟{ //鸵鸟不会飞 }
//示例2
A类{ 保护功能添加($a,$b){ 返回$ a+$ b; } }
//过载 乙类扩展了甲类
受保护的函数add($a,$b){ 返回$ a+$ b+100; } } Richter替换原则是对类继承的约束。对里克特替换原理有两种理解:
1.您不能用冗余的方法或属性继承不合适的类(示例1)。
2.子类可以扩展父类的功能,但不能改变父类的原有功能(例2)。
里克特替换原则包含以下隐藏的含义:
1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2.子类可以添加自己独特的方法。
3.当子类的方法重载父类的方法时,方法的前提条件(即方法的形式参数)比父类方法的输入参数宽松。
4.子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)比父类更严格。
依赖倒置的原理是什么?
简单来说,一个类不应该强制依赖另一个类,每个类对于另一个类都是可替换的。
具体概念:
1.上层模块不应该依赖于下层模块,但是它们都依赖于一个抽象(父类不能依赖于子类,但是它们都依赖于抽象类)。
2.抽象不能靠具体,具体要靠抽象。
为什么要依赖接口?因为接口体现了问题的抽象性,又因为抽象性一般比较稳定或者变化比较少,所以具体是可变的。所以依赖抽象是代码扩展和运行时绑定(多态)的基础:只要实现了抽象类的子类,就可以被类消费者使用。
& lt?服务器端编程语言(Professional Hypertext Preprocessor的缩写)
界面员工 { 公共职能工作; }
班主任实现员工//具体应该是依赖的和抽象的 { 公共函数工作{ echo & # 39教学...'; } }
类编码器实现雇员 { 公共函数工作{ echo & # 39编码...'; } }
类别工作//示例1 { 公共职能工作{ $教师=新教师; $ teacher-work; } }
workB//类示例2 { private $ e; 公共功能集(员工$e){ $ this-e = $ e; }
公共职能工作{ $ this-e-work; } }
$工作狂=新工作狂;//工作狂靠老师上课,不符合依存倒置原则 $ work a-work; $workb =新WorK b;//workB不依赖于一个类,这个类可以注入老师,也可以注入编码器。 $ workb-set(new teacher); $ workb-work;在工作法(例1)中,工作方法取决于老师实施;在workB(示例2)中,工作依赖于抽象,因此所需的对象可以通过参数传递。以上代码通过接口实现了一定程度的解耦,但还是有限的。不仅使用接口,使用工厂也可以在一定程度上实现解耦和依赖反转。
在workB中,教师实例通过set方法传入,从而实现工厂模式。由于这个实现还是硬编码的,为了进一步扩展代码,在配置文件中写下这个依赖关系,表示workB需要一个teacher对象,这个对象由程序专门配置是否正确(比如依赖类文件是否存在)以及加载配置时依赖的实现。这个检测程序叫IOC容器(这里不清楚IOC的朋友可以自己谷歌)。
接口隔离的原理是什么?
它的核心思想是用几个小的专用接口代替一个大的总接口(只关心接口,不关心实现)。
接口隔离的原则体现在:
1.接口应该是内聚的,应该避免“胖”接口。
2.不要被迫依赖不用的方法,这是一种接口污染。
3.它表明,不应该强迫客户端实现一些未使用的接口,而是应该将fat接口分组,并用多个接口替换它们,每个接口服务于一个子模块。简单来说,使用多个专用接口比使用单个接口好得多。
主要有两种隔离方法:1。委托分离,通过增加一个新的类型来分离客户和接口的直接依赖,但是会增加系统开销(委托的概念用在设计模式中,比如代理模式和策略模式,好奇的朋友可以自己谷歌,这里就不贴代码了)。
2.最好是分离多重继承,通过接口的多重继承来实现客户的需求。
fat接口的示例描述
& lt?服务器端编程语言(Professional Hypertext Preprocessor的缩写) 动物接口{ public function walk; public function speak; }
//实现一个狗的接口
类狗工具动物{ 公共函数walk{ 回声狗会走路; } 公共函数speak{ 回声狗会说话; } }
//好了,现在我们要创建一条鱼。它会游泳。我们做什么呢 //我们要修改接口,这也会影响dog类的实现,fish也需要实现walk和speak方法,如下代码所示:
动物接口{ public function walk; public function speak; public function swim; }
//修改后的Gog类 类狗工具动物{ 公共函数walk{ 回声狗会走路; } 公共函数speak{ 回声狗会说话; } 公共函数swim{ } }
//鱼 鱼类工具动物类{ 公共函数walk{ } 公共函数speak{ } 公共函数swim{ 回声鱼会游泳; } }此时的Animal接口类呈现出“胖”接口的特征。所谓胖接口,就是接口定义了所有实现类都不需要的方法,就像动物接口类,有的动物不会游泳,有的动物不会走路,有的动物不会飞。如果所有这些方法都写在一个Animal接口类中,那么后面的扩展和维护将是一场灾难。
那么,如何解决以上问题呢?
很简单,只要细化接口,把Animal接口类拆分成三个接口类,然后用多重继承把接口分开就行了。
& lt?服务器端编程语言(Professional Hypertext Preprocessor的缩写) 接口animalCanSpeak{ public function speak; }
接口AnimalCanSwim{ public function swim; }
接口animalCanSpeak{ public function speak; }
//定义了这些接口类之后,狗和鱼的实现就容易多了
//狗 类Dog实现animalCanSpeak,animalCanWalk{ 公共函数walk{ 回声狗会走路; } 公共函数speak{ 回声狗会说话; } }
//鱼 Fish类实现了AnimalCanSwim{ 公共函数swim{ 回声鱼会游泳; } }接口隔离原则(ISP)的概念:使用多个专用接口,而不是单一的通用接口,即客户端不应该依赖那些不需要的接口。
使用接口隔离原则时,需要注意控制接口的粒度,接口不能太小,太小会导致系统中接口溢出,不利于维护;接口不能太大,太大的接口会违反接口隔离原则,灵活性差,使用不方便。一般来说,接口只包含为特定类型的用户定制的方法,客户不应该被迫依赖那些他们不使用的方法。
结论实践真理