开闭原则:一个类、模块和函数应该对修改封闭,对扩展开放。
单一职责原则:一个类只做一件事,一个类应该只有一个引起它修改的原因。
里氏替换原则:子类应该可以完全替换父类。也就是说在使用继承时,只扩展新功能,而不要破坏父类原有的功能。
依赖倒置原则:细节应该依赖于抽象,抽象不应依赖于细节。把抽象层放在程序设计的高层,并保持稳定,程序的细节变化由底层的实现层来完成。
迪米特法则:又名「最少“知道”原则」,一个类不应知道自己操作的类的细节,换言之,只和朋友谈话,不和朋友的朋友谈话。
接口隔离原则:客户端不应依赖它不需要的接口。如果一个接口在实现时,部分方法由于冗余被客户端实现,则应该将接口拆分,让实现类只需依赖自己需要的接口方法。
所有的设计模式都是为了程序能更好地满足这六大原则。设计模式有很多种,大体分为三类。
一、构建型模式包含五种工厂模式
抽象工厂模式
单例模式
建造型模式
原型模式
1、工厂模式
在平时写代码过程中,构建对象最常用的方式是new一个对象。这属于一种硬编码。每new一个对象,相当于调用者就知道了一个类,增加了类与类之间的联系,不利于程序的松耦合。其实构建过程可以被封装起来,工厂模式便是用于封装对象的设计模式。1.1简单工厂模式例如:当我们需要一个苹果时,我们需要知道苹果的构造方法,需要一个梨子时,需要知道梨子的构造方法。更好的实现方式是有一个水果工厂,我们告诉工厂需要什么种类的水果,水果工厂将我们需要的水果制造出来给我们就可以了。这样我们就无需知道苹果、梨子是怎么种出来的,只用和水果工厂打交道即可。将构建过程封装的好处不仅可以降低耦合,加入某个产品构造方法相当复杂,使用工厂模式可以大大减少代码重复。比如,如果生产一个苹果需要苹果种子、阳光、水分,将工厂修改如下:
调用者的代码则完全不需要变化,而且调用者不需要在每次需要苹果时,自己去构建苹果种子、阳光、水分以获得苹果。苹果的生产过程再复杂,也只是工厂的事。这就是封装的好处。简单工厂模式就是让一个工厂类承担构建所有对象的职责。它的弊端也显而易见:如果需要生产的产品过多,此模式会导致工厂类过于庞大,承担过多的职责,变成超级类。当苹果生产过程需要修改时,要来修改此工厂。梨子生产过程需要修改时,也要来修改此工厂。也就是说这个类不止一个引起修改的原因。违背了单一职责原则。
当要生产新的产品时,必须在工厂类中添加新的分支。而开闭原则:类应该对修改封闭。我们希望在添加新功能时,只需增加新的类,而不是修改既有的类,所以这就违背了开闭原则。
1.2工厂方法模式为了解决简单工厂模式的这两个弊端,工厂方法模式应运而生,它规定每个产品都有一个专属工厂。比如苹果有专属的苹果工厂,梨子有专属的梨子工厂。这样和直接new出对象有什么区别?上文说工厂是为了减少类与类之间的耦合,让调用者尽可能少的和其他类打交道。用简单工厂模式,我们只需要知道FruitFactory,无需知道Apple、Pear类,很容易看出耦合度降低了。但用工厂方法模式,调用者虽然不需要和Apple、Pear类打交道了,但却需要和AppleFactory、PearFactory类打交道。有几种水果就需要知道几个工厂类,耦合度完全没有下降,甚至还增加了代码量!工厂模式的第二个优点在工厂方法模式中还是存在的。当构建过程相当复杂时,工厂将构建过程封装起来,调用者可以很方便的直接使用,例:调用者无需知道苹果的生产细节,当生产过程需要修改时也无需更改调用端。同时,工厂方法模式解决了简单工厂模式的两个弊端。当生产的产品种类越来越多时,工厂类不会变成超级类。工厂类会越来越多,保持灵活。不会越来越大、变得臃肿。如果苹果的生产过程需要修改时,只需修改苹果工厂。梨子的生产过程需要修改时,只需修改梨子工厂。符合单一职责原则。
当需要生产新的产品时,无需更改既有的工厂,只需要添加新的工厂即可。保持了面向对象的可扩展性,符合开闭原则。
1.3抽象工厂模式工厂方法模式进一步优化然后苹果工厂和梨子工厂都实现此接口:
此时,调用者可以将AppleFactory和PearFactory统一作为IFactory对象使用。
我们在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类,只需要将此工厂当作抽象的IFactory接口使用。这种经过抽象的工厂方法模式被称作抽象工厂模式。由于客户端只和IFactory打交道了,调用的是接口中的方法,使用时根本不需要知道是在哪个具体工厂中实现的这些方法,这就使得替换工厂变得非常容易。我们把上面的苹果工厂替换成梨子工厂:IFactory中只有一个抽象方法,这个是比较简单的工厂。实际上抽象工厂模式主要用于替换一系列方法。例如将程序中的SQLServer数据库整个替换为Access数据库,使用抽象方法模式的话,只需在IFactory接口中定义好增删改查四个方法,让SQLFactory和AccessFactory实现此接口,调用时直接使用IFactory中的抽象方法即可,调用者无需知道使用的什么数据库,我们就可以非常方便的整个替换程序的数据库,并且让客户端毫不知情。抽象工厂模式很好的发挥了开闭原则、依赖倒置原则,但缺点是抽象工厂模式太重了,如果IFactory接口需要新增功能,则会影响到所有的具体工厂类。使用抽象工厂模式,替换具体工厂时只需更改一行代码,但要新增抽象方法则需要修改所有的具体工厂类。所以抽象工厂模式适用于增加同类工厂这样的横向扩展需求,不适合新增功能这样的纵向扩展。