针对咱们设计的功能模块对扩展开放,对修改关闭:利用面向接口(抽象)编程(多态的特性),实现对功能需求扩展的同时,不容许更改原来的代码。提升对象的可复用性、可维护性、灵活性。html
抽象的说,用抽象思惟构建咱们想要建立的实体对象,用具体实现去扩展实体对象的细节实现。java
具体的说,业务需求的功能能够去扩展,可是已经实现的功能不该该去修改。程序员
需求:以简单工厂模式和工厂方法模式理解开闭原则。spring
分析:编程
工厂模式是获取一个实例对象的建造者模式,咱们不关心对象如何产生,只关心结果。app
简单工厂模式是面对比较简单的业务场景,用于获取一个对象实例。咱们须要传入相关业务参数,让简单工厂对象去作相应的判断,而后返回给咱们对应的实例对象。这个工厂对象作了不少事情,他违背了单一职责原则,当咱们的业务须要扩展的时候,这个工厂对象必须去修改,新增业务判断,新增返回对应的实例对象。这就违背了开闭原则。ide
public class FruitsFactory { /** * @description: * @param fruitsName * @return com.lmc.gp13280.pattern.factory.fruits.IFruits * @date 2019/5/23 15:44 * @author lmc */ public IFruits product(String fruitsName){ if(null != fruitsName && fruitsName != ""){ System.out.println("客户需求的水果是"+fruitsName); if("苹果".equals(fruitsName)){ System.out.println("水果工厂生产了苹果"); return new Apple(); } if("橙子".equals(fruitsName)){ System.out.println("水果工厂生产了橙子"); return new Orange(); } if("香蕉".equals(fruitsName)){ System.out.println("水果工厂生产了香蕉"); return new Banana(); } return null; }else{ return null; } } }
上面是一个水果工厂,我若是想要草莓,就必须的修改代码去实现草莓的生产了。工具
工厂方法模式是抽象一个工厂接口,让具体的工厂子类实例对象实现工厂接口。咱们想要一个具体的实例对象,只须要找相应的子类工厂实现类就能够了,若是须要业务扩展,咱们不须要修改原来的工厂子类,只须要新增新的工厂子类就好了。这就实现了业务扩展,可是不会修改原来的程序逻辑。遵循了开闭原则和单一职责原则。测试
public interface IFruitsFactory { /** * 工厂的生产方法 * @return IFruits */ IFruits product(); }
public class AppleFactory implements IFruitsFactory { @Override public IFruits product() { System.out.println("苹果工厂只生产苹果"); return new Apple(); } }
public class BananaFactory implements IFruitsFactory { @Override public IFruits product() { System.out.println("香蕉工厂只生产香蕉"); return new Banana(); } }
public class OrangeFactory implements IFruitsFactory { @Override public IFruits product() { System.out.println("橙子工厂只生产橙子"); return new Orange(); } }
上面的示例代码中,咱们要新增具体的水果工厂去扩展业务,只须要新增对应的工厂子类去实现水果工厂的接口就好了。ui
实现业务扩展,不改变原来的程序就是遵循开闭原则。
spring的依赖注入相信你们都不陌生,其实依赖倒置原则是程序对象依赖其余对象的时候,应该依赖其抽象,不要依赖实现,应该依赖顶层对象,不要依赖具体的底层对象。由于程序的具体实现都是实现类去实现的,可是咱们要去依赖实现类的顶层接口对象,这就是倒置,也就是依赖倒置。
依赖倒置原则的核心是运行时多态,程序在编译时不会去实例化子类对象,在程序运行的时候虚拟机才会去选择实例化具体的子类对象。
在程序设计中,通常接口定义好了,就不会去轻易改变。由于,在一个成熟的系统中,改变接口,就至关于把设计推翻重构了,你愿意作这样的事情?可是实现类,我们能够进行轻微改动,或者说不改,新增一个新的实现类去代替。若是依赖其实现类,只要实现类改动,那么程序员的灾难是否是来了呢?若是依赖其顶层接口,咱们其余依赖此接口的代码都不须要去作任何改动,由于接口没变啊,只须要改动需求具体的实现业务逻辑,或者新增业务子类实现接口。是否是能够提早下班了。
经过依赖倒置,能够减小类与类之间的耦合性,提升系统的稳定性,提升代码的
可读性和可维护性,并可以下降修改程序所形成的风险。
/** * 水果店 * @author: maocheng.liao * @create: 2020-02-23 15:20 */ public class FruitShop { private IFruitsFactory fruitsFactory; public IFruitsFactory getFruitsFactory() { return fruitsFactory; } public void setFruitsFactory(IFruitsFactory fruitsFactory) { this.fruitsFactory = fruitsFactory; } /** * @description: * @param :从水果生产基地去进货水果 * @return com.lmc.gp13280.pattern.factory.fruits.IFruits * @date 2020/2/23 15:34 * @author maocheng.liao */ public IFruits getFruits(){ IFruits fruits = fruitsFactory.product(); return fruits; } }
/** * 水果店依赖倒置测试 * * @author: maocheng.liao * @create: 2020-02-23 15:25 */ public class FruitShopTest { public static void main(String[] args) { IFruitsFactory fruitsFactory= new AppleFactory(); FruitShop fruitShop=new FruitShop(); fruitShop.setFruitsFactory(fruitsFactory); IFruits apple = fruitShop.getFruits(); apple.getName(); fruitsFactory = new BananaFactory(); fruitShop.setFruitsFactory(fruitsFactory); IFruits banana = fruitShop.getFruits(); banana.getName(); fruitsFactory = new OrangeFactory(); fruitShop.setFruitsFactory(fruitsFactory); IFruits orange = fruitShop.getFruits(); orange.getName(); } }
上面的代码就是最简单的依赖倒置的实现,咱们依赖的是水果工厂接口和水果接口,水果店是在水果生产基地去进货水果,至于进货什么水果,取决于水果生产基地生产什么水果和水果店想进什么水果。而不是依赖具体的水果工厂实现类和具体的水果实现类。
我以为单一职责是迪米特法则的一种体现。
单一职责原则:见名知意,专人作专事,不要多管闲事。对于类、接口、方法只负责一项本身的职责。对于本身的职责程序修改以后,不会影响到其的职责程序。高内聚,低耦合的程序必须遵循单一职责,分工明确,可读性、可维护性高。
最简单的示例:我们人的手脚代替不了眼睛,眼睛也代替不了手脚。
臃肿这个词是否是能很好的体现出单一职责原则呢?代码写的这么臃肿,还能不能好好玩耍啦。
我以为接口隔离原则是单一职责原则的一种体现,也是迪米特法则的一种体现。
面向对象编程,万物皆对象。
接口隔离原则:定义多个细化而明确分工的专门接口,不要去定义一个单一臃肿的接口。对于臃肿的接口,咱们很差具体实现,这样确定会违背单一职责原则。
最简单的示例:对于家用小汽车对象来讲,往大的说,他属于交通工具类、他属于车类、它属于汽车类。往小的说,它属于家用小汽车类、私家车类。
迪米特法则又叫最少知道原则,也有单一职责和接口隔离原则的味道。
迪米特法则:低耦合,如何作到极致呢,那就是只依赖本身真正有关系的对象。在我职责以外的对象所有拒之门外。
我以为这个没啥好说的,作到了单一职责,迪米特法则很好实现吧。
官方定义:任何基类(父类)能够出现的地方,子类必定能够出现。 里氏替换原则是继承复用的基石,只有当衍生类能够替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也可以在基类的基础上增长新的行为。当一个子类的实例应该可以替换任何其超类的实例时,它们之间才具备is-A关系。
本人理解: 里氏替换原则是为了约束继承泛滥,就是说,在面向对象程序设计中,必须具备is-A的关系才能去使用继承,而不能去为了实现某一业务需求,为了方便使用继承而继承。
前置条件:每一个方法调用以前,该方法应该校验传入参数的正确性,只有正确才能执行该方法,不然认为调用方违反契约,不予执行。这称为前置条件(Pre-condition
)。
后置条件:一旦经过前置条件的校验,方法必须执行,而且必须确保执行结果符合契约,这称之为后置条件(Post-condition
)。也就是返回值符合。
里氏替换原则约束条件:当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;而子类中方法的后置条件必须与超类中被覆盖的方法的后置条件相同或者更为严格。
不要为了继承而继承,而是真的有继承关系的时候才能去继承。
这里结合依赖倒置原则使用合成复用原则效果更佳。
组合(has-a):类比电脑组装,内存、硬盘、cpu
、显卡 ······ 它们之间组成了电脑,可是他们之间没有什么关系。这就是组合关系,生命周期一致。
聚合(contanis-a
):类比员工聚合成部门。员工和部门是相互独立的,一两个员工离职不影响部门对象。部门解散也不影响员工对象。
合成复用原则:程序复用尽可能使用组合关系、聚合关系。尽可能少使用继承关系去实现业务需求。
继承会把全部的实现所有暴露给子类。破坏了类的封装性,父类的改变,会影响子类。高耦合。
组合、聚合能够很好的遵循依赖倒置原则,开闭原则。成员对象的细节实现(组合、聚合对象)对于一个新的对象是隐藏的。
合成复用原则配合依赖倒置原则,很好的遵循开闭原则。达到程序高可用、高可维、高可读。
spring为何这么牛?spring为何依赖注入?spring为何是轻量级的?