面向对象:设计模式 23

在工作初期就看了关于设计模式的书《大话设计模式》简单易懂,但是后来对一些设计模式渐渐模糊,记得最多或者大家说的最多的像单例、工厂记忆深刻,现在回头再看一遍,别以一番风味,理解又有不同,这里个人简单进行记录,其中间杂着个人的一些见解。

关于例子,大家可以去找这边本看看,或者直接到git上clone一下,地址:https://github.com/PengfeiLiOnGit/designpattern

设计模式除了23种外,肯定还有衍生出来新的设计模式,但是万武归宗,实则都是对类的抽象。

面向对象设计模式体现的就是抽象的思想,类是对对象的抽象,抽象类是对类的抽象,接口是对行为的抽象

一、UML 类图:

二、原则

单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏。

开放-封闭:软件实体(类、模块、函数)应该是可以扩展,但是不可以修改。面对需求,对程序的改动是通过新代码进行的,而不是更改现有代码。

依赖倒转原则:抽象不应该依赖细节,细节应该依赖与抽象。(针对接口编程,不要对实现编程)。

高层模块依赖底层模块-这是对的,但是如果是针对实现变成,比如数据库访问依赖某个数据操作,如果一旦更改数据库,意味着高层数据库访问也需要改动,如果把高层和底层都进行抽象化,针对接口变成,那么如果要更改西其他数据库,只需要针对底层访问接口增加新实现就可以,不要改动原有的代码。

里氏代换原则:一个软件实体如果使用的是一个父类的话,那么一定适用与其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,在软件里面,把父类都替换成它的子类,程序行为没有变化,简单的说,子类型必须能够替换掉它们的父类型

迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以使用第三者转发这个调用。(最少知识原则)

合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承。聚合表示弱的“拥有关系”,体现的是A对象可以包涵B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分与整体的关系,同时部分和整体的生命周期一样。

三、设计模式

1、简单工厂:

针对不同相同的行为但是不同的算法进行抽象,利用一个简单工厂获取实际的运算类。

2、策略:针对不同的算法进行封装、让它们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户。

 关键点在于context 通过聚合抽象策略类的引用,请求者只需要知道context的存在就可以了,具体的配置再context进行判断,想比较简单工厂,耦合度再次降低。

当然在context中同样可以再次套用其他模式避免switch分支,比如状态模式等再次进行解耦,或者通过反射。

3、装饰模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活。

关键点在于针对具体的对象,衍生于抽象装饰类,其类继承统一的抽象接口,并且在装饰的实现类中对接口进行实现。在实际调用过程中,针对实际对象进行调用统一的装饰接口。

并且在抽象装饰类中需要对对象接口进行引用,从而达到装饰,也就添加逻辑的效果。

实际场景中在对某些现有功能需要增加业务的或者功能的时候,增加新的功能实现即可,而不需要修改原有抽象实现,去除原有方法中过多的职责。

4、代理模式:为其他对象提供一种代理以控制对这个对象的访问。

 关键点实际对象与代理同时实现相同的接口,同时代理对象保持对实际对象引用。

在实际环境中实际对象中可能会有很多私有逻辑,而请求者只知道代理而不知道实际对象。

5、工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

 相比较简单工厂,工厂模式针对工厂进行抽象,消除了判断与条件分支,若需要扩展实际产品,至需要实现product和creator,而不要修改原有工厂类代码。

6、原型模式:

 实际java使用中通过实现cloneAble接口,进行对象的拷贝工作。注意点在于,在实现过程中需要注意浅拷贝与深拷贝的问题。

7、模版方法模式:模版方法在实际使用过程中经常使用,也就是继承的特点决定的。抽象类实现---- 子类重写

8、外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更多容易使用。

 关键点自在于高层的外观类引用具体的关联对象,实现子系统的功能,并处理具体的任务。

实际场景中组合多个接口,同一在外观类中进行业务处理,最终开放给外部调用者。有点类似于adapter 适配器,但是适配器的思想是针对某个负责的接口同一为一个新的接口,适配器同时是拥有兄弟关系的。

9、建造者模式:将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

 关键点在于director对象,具体对象构造由它进行实现,虽然通过工厂模式也可实现,通过director 会显示更大的灵活性,在指挥者对象中需要具有build的引用。

实际应用场景,建造抽象过程相同,但是具体的建造细节略有不同,即可以使用这种设计模式。

 10、观察者模式:发布-订阅模式,定义一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象。这个主题对象在状态发生变化的时,会通知所有观察者对象,使它们能够自动更新自己。

11、抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

基于工厂模式再次对工厂进行抽象,实现的工厂与实现的生产对象再进行对应。抽象工厂比较臃肿,虽然实现了解耦,但是如果需要进行扩展,工作量也会非常之大,相比之下,利用泛型与反射,也已更加简便的实现。

12、状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

 关键点在于context 是state的聚合引用,在实例调用handle处理的时候对state进行判断,并更新context,并更改处理方式。

例如初始化状态为A,A的处理方式如果不满足就设置context中引用的状态为B或C等,根据不同的状态从而使改变实现的方式,对于需要大量条件判断的情况下尤为适用

13、适配器模式:将一个类的接口转换成客户端希望的另外一个接口。解决接口不兼容的问题。

 在外观模式中提到,对于已经存在的接口不兼容的情况下进行使用,通过adapter类对原有的接口进行转换。

实际场景,比如使用了第三方的接口,或者其他同事或者原有接口,由于其实现由于各种原因不能进行修改,所以使用适配器模式,则不需要关注具体实现。

 14、备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象回复到原先保存的状态。

作为备忘录的聚集,实际保留备忘录的引用,为什么要存在memento而不使用发起者的原型复制,原因在于再实际过程中不一定要保存所有信息,可能只是保留一个状态,所以通过memento 实际存储这些信息,

而通过聚集的引用在caretaker中,在需要恢复的时候从其中获取即可。

15、组合模式:将对象组合成树形结构以标识‘部分-整体’的层次接口。组合模式使得用户对单个对象和组合对象的使用具有一致性。

分支节点与叶子节点同事实现component接口,同时分支节点需要具有对对象接口的聚集引用。

16、迭代器模式:提供一种方法顺序的访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

迭代器在实际应用中需要的不多,因为大家面向对象语言中都已经实现了迭代器,迭代器中需要具有下标,与next方法来获取对象,同时根据isDone 来判断是否已经结束。

17、单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点

一般单例对象使用懒加载和DSL 双锁机制,来避免在多线程中生成多个对象。

如果使用饥渴模式的话无需考虑。

public static Singleton getInstance(){
        // 在多线程访问的情况下利用DSL 双重锁机制保证实例唯一
        // 因为是静态方法,所以使用类锁,或者使用lock 锁,与生成一个对象锁
        if(singleton == null){
            // 处理业务逻辑

            // DSL
            synchronized (object){
                if(singleton==null){
                    singleton = new Singleton();
                }
            }
        }

        return singleton;
    }

18、桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立的变化。

实现系统可能有多角度分析,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

比如手机可以安装品牌区分,同事每个品牌手机中又拥有相似的功能,但是具体的功能在不同品牌中实现不同,那么就可以使用桥接模式进行体现。

手机品牌的抽象中聚合手机软件、功能的引用。

利用聚合/复用原则进行弱引用,这样增加品牌或者增加手机功能都不需对原有的代码进行修改,直接扩展即可。

19、命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

把命令进行抽象,命令的记录与撤销记录在Receiver接收者中,同事在命令中具有最终调用者的引用,下面是书中的截图引用。

20、职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象练成一条链,并沿着这条链传递该请求,知道又一个对象处理它为止。

 关键点在于在实现类中需要具有父类的引用,在实现处理请求的时候如果不满足条件,则重置处理实现。

21、中介模式:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

两个对象进行交互同时通过中介者进行处理,多个对象只与中介对象进行通信,具体的通信由中介对象进行处理。

22、享元模式:运用共享技术有效的支持大量细粒度的对象。

 特点对于某一类对象通过共享实例的方式避免内存的过度滥用,同时与单例模式结合,针对需要重复引用的对象共享他们。

创建实例的事物可以交给子类,然后由享元工厂提供接口进行分法。

23、解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

实际使用场景不多,是一种转换模式,比较出名的例子是正则表达式。

24、访问者模式:表示一个作用与某对象接口中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用与这些元素的新操作。

 针对特定的对象,进行抽象分类,同时在扩展状态的时候可以非常容易的进行扩展,但是如果要条件ele对象则会造成大量的工作。

此模式针对特定且固定分类的对象比较适用。

例如,男人和女人,但是会有很多中的状态,就比较适合这种设计模式。

原文地址:https://www.cnblogs.com/jony-it/p/11477098.html