我们在使用考虑使用设计模式的时候应先遵循6种原则:单一职责原则、开放封闭原则、依赖倒转原则、里氏代换原则、合成聚合复用原则、迪米特原则。
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。比如我们写一个窗体应用程序,应该只负责窗体,不要把一些功能处理的方法也加进去。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责。
开放-封闭原则:面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。也就是说软件实体应该可以扩展,但是不可修改。我们应该仅对程序中呈现出频繁变化的那些部分做出抽象,然而,对于应用程序中的每个部分都刻意地进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。
依赖倒转原则就是要针对接口(抽象)编程,不要对实现(细节)编程。也可以说是抽象不应该依赖细节,细节应该依赖抽象。即程序中所有的依赖关系都是终止于抽象类或者接口。举个例子,我们电脑中的CPU、内存等都是在针对接口设计的,如果针对实现来设计,CPU、内存就要对应到具体的某个品牌的主板,那就会出现换内存需要把主板也换了的尴尬。所以,依赖倒转其实就是谁也不要依靠谁,除了约定的接口。例如,收音机就是典型的耦合过度的产品,因为各个部件都相互依赖,很难维护。
迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承。优先使用对象的合成/聚合将有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。
聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。比方说,大雁有两只翅膀,翅膀与大雁是部分和整体的关系,并且它们的生命周期是相同的,于是大雁和翅膀就是合成关系。而大雁是群居动物,所以每只大雁都是属于一个雁群,一个雁群可以有多只大雁,所以大雁和雁群是聚合关系。
设计模式:
1、装饰模式:动态地给一个对象添加一些额外的职责
2、代理模式:相当于真实对象的代表。
3、工厂方法模式就是简单工厂模式的进一步抽象和推广。工厂方法模式的存在是为了克服简单工厂模式所违背的开放-封闭原则。它是把工厂类演变为一个接口,然后通过子类创建接口中方法的各种实现工厂。
4、模板方法模式:通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。此模式就是提供了一个很好的代码复用平台,去除一个类中没必要的代码重复。碰到这种情况,当不变和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类拜托重复的不变行为的纠缠。
5、外观模式:又称门面模式。为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。此模式完美地体现了依赖倒转原则和迪米特法则的思想。
6、建造者模式:建造的‘过程’是稳定的,而具体建造的‘细节’是不同的。也就是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。
7、观察者模式:又叫发布-订阅模式。定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。使用此模式的情况如下:
①当一个对象的改变需要同时改变其他对象,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式
②当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
作用:观察者模式所做的工作其实就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。观察者模式应与事件委托同时使用。
8、工厂方法模式:是定义一个用于创建对象的接口,让子类决定实例化哪一个类。
9、抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。优点:①易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。②它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。编程是门艺术,这样大批量的改动,显然是非常丑陋的做法。
10、状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。状态模式的作用:状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
好处:将与特定状态相关的行为局部化,并且将不同状态的行为分割开来。
使用:当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。另外,如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是大量的多分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类。这样,这些对象就可以不依赖于其他对象而独立变化了,某一天客户更改需求,只需增加或减少业务状态或改变状态流程即可。
11、适配器模式:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。它的作用就是使得一个东西适合另一个东西的东西,比如:笔记本的电源适配器。
使用:系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。意思就是说当两个类所做的事情相同或相似,但是具有不同的接口时要使用它。这样的话由于类都共享同一接口,使得客户代码可以统一调用同一接口就行了,这样可以更简单、更直接、更紧凑。适配器应在只有碰到无法改变原有计划和代码的情况时才对其进行考虑。关于适配器有两种类型:类适配器模式和对象适配器模式。
12、备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
结构图:
①:Originator(发起人):负责创建一个备忘录
②:Memento(备忘录):负责记录发起人对象的内部状态,并可防止发起人以外的其他对象访问备忘录。相当于一个存储箱
③:Caretaker(管理者):负责保存好备忘录,不能对备忘录的内容进行操作或检查。
使用:备忘录模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。
13、组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
14、迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。在Java中foreach in就是一个典型的迭代模式
15、单例模式(多线程时的单例):我们可以给进程加一把锁来处理。这里需要解释一下lock语句的含义,lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
代码实现:
public class Singleton {
private static Singleton instance;
private static final Object sysncRoot = new Object();
private Singleton(){}
public static Singleton GetInstance(){
//加两个判断是为了双重锁定
if(instance == null){
//判断instance实例有没有被创建过实例
lock(syncRoot){
if(instatnce == null){
instance = new Singleton();
}
}
}
return instance;
}
}
16、桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。即实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。
17、命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
优点:①它能较容易地设计一个命令队列;②在需要的情况下,可以较容易地将命令记入日志;③允许接收请求的一方决定是否要否决请求;④可以容易地实现对请求的撤销和重做;⑤由于加紧新的具体命令类不影响其他的类,因此增加新的具体命令类很容易;⑤命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开。
18、职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
好处:请求者不用管哪个对象来处理,反正该请求会被处理就对了。这就使得接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构,结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需要保持它所有的候选接收者的引用。这也就大大降低了耦合度这就相当于显示生活中的快递以及信件的派送等。
19、中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。此模式就相当调停各国关系的联合国组织。
20、享元模式:运用共享技术有效地支持大量细粒度的对象。如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式。
21、解释器模式:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
22、访问者模式:表示一个作用于某对象结构中各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式适用于数据结构相对稳定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。
目的:要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
优点:访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
慎用!只有当真正需要它的时候再使用。此模式使增加新的数据结构变得困难了。
23、策略模式:封装了变化。只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。策略模式与工厂模式的区别在于,工厂模式只是创建对象,而策略模式把创建对象以及方法调用都封装起来,这样的话就彻底的与客户端分离,减轻了客户端的负担。
顺便补充一下Java的相关知识点:
1、在做if判断是考虑是否可用switch代替
2、要达到可维护、可复用、可扩展、灵活性好我们应当尽量细分化代码,降低代码之间的耦合度
3、面向对象的三大特性为封装、继承和多态
4、既然用了继承,并且这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复。
5、面向对象设计其实就是希望做到代码的责任分解。即单一职责原则
6、代码无错未必优
7、编码融入生活