一、设计模式的六大原则


开闭原则(Open Close Principle)

    对扩展开放,对修改关闭。

    在程序需要进行拓展的时候,不能去修改原有的代码,

    想要达到这样的效果,需要使用接口和抽象类。



依赖倒转原则(Dependence Inversion Principle)

    针对接口编程,依赖于抽象而不依赖于具体,

    是开闭原则的基础。



里氏替换原则(Liskov Substitution Principle):

    任何父类可以出现的地方,子类一定可以出现。 只有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。LSP是继承复用的基石。

    1. 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。

    2. 子类中可以增加自己特有的方法。

    3. 子类的方法重载父类的方法时,方法的形参要比父类方法的输入参数更宽松。

    4. 子类的方法实现父类的抽象方法时,方法的返回值要比父类更严格。



接口隔离原则(Interface Segregation Principle)

    1.一个类对另外一个类的依赖性应当是建立在最小的接口上的。

    2.使用多个专门的接口比使用单一的总接口要好。

    3.一个接口代表一个角色,不应当将不同的角色都交给一个接口。

    (没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染)。

    4.不应该强迫实现类依赖于它们不用的方法。如果强迫实现类使用它们不使用的方法,那么这些实现类就会面临由于这些不使用的方法的改变所带来的改变。



迪米特法则(Law of Demeter)、
最少知道原则(Least Knowledge Principle 简写LKP)

    就是一个类对自己依赖的类知道的越少越好。

    对于被依赖的类来说,尽量地的将逻辑封装在类的内部,

    对外除了提供的public方法,不对外泄漏任何信息。

    迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。


参考:http://blog.csdn.net/zhengzhb/article/details/7296930



合成/聚合复用原则(Composite Reuse Principle, CRP)

    在实现复用时,尽量使用合成/聚合的方式,而不是使用继承。

    对象的继承关系在编译时就定义好了,所以无法在运行时改变从父类继承的子类的实现。

    子类的实现和它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。

    当你复用子类的时候,如果继承下来的实现不适合解决新的问题,则父类必须重写或者被其它更适合的类所替换,这种依赖关系限制了灵活性,并最终限制了复用性。


参考:http://blog.csdn.net/lovelion/article/details/7563441

参考:http://blog.csdn.net/u010832572/article/details/45007933



二、简述23种设计模式


====创建型模式====


1、工厂方法模式(Factory Method)


    1.普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。


    2.多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。


    3.静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。


    总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。



2、抽象工厂模式(Abstract Factory)

    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

    其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!


3、单例模式(Singleton)

    单例对象能保证在同一个JVM中,该对象只有一个实例存在。


        这样的模式有几个好处:

            1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

            2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

            3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。


        通过单例模式的学习告诉我们:

            1、单例模式理解起来简单,但是具体实现起来还是有一定的难度。

            2、synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。

            3、单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。

            4、单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。



4、建造者模式(Builder)

    工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。


    从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工厂模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定



5、原型模式(Prototype)

    将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

    原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。

    很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native(本地方法)的,


    浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。

    深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。



====结构型模式====


6、适配器模式(Adapter)

    将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

    类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。


    对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper(包装)类,持有原类的一个实例,在Wrapper(包装)类的方法中,调用实例的方法就行。


    接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。



7、装饰模式(Decorator)

    给一个对象动态的增加一些新的功能,

    要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,

    装饰器模式的应用场景:

        1、需要扩展一个类的功能。

        2、动态的为一个对象增加功能,而且还能动态撤销。

            (继承不能做到这一点,继承的功能是静态的,不能动态增删。)

    缺点:产生过多相似的对象,不易排错!



8、代理模式(Proxy)

    代理模式就是多一个代理类出来,替原对象进行一些操作

    比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。

    代理模式的应用场景:

    如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

        1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。

        2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

        使用代理模式,可以将功能划分的更加清晰,有助于后期维护!



9、外观模式(Facade)

    外观模式是为了解决类与类之间的依赖关系的,外观模式就是将他们的关系放在一个Facade(外观)类中进行维护。

    例如主机持有CPU、内存、硬盘等

    外观模式是为了解决类与类之间的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade外观类中,降低了类类之间的耦合度,该模式中没有涉及到接口,

    我们以一个计算机的启动过程为例:

    如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这,就是外观模式!



10、桥接模式(Bridge)

    桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。



11、组合模式(Composite)

    组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,

    使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。



12、享元模式(Flyweight)

    享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

    FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。



====行为型模式====


13、策略模式(strategy)

    策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数,

    策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。



14、模板方法模式(Template Method)

    解释一下模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用



15、观察者模式(Observer)

    当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。



16、迭代子模式(Iterator)

    顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问



17、责任链模式(Chain of Responsibility)

    接下来我们将要谈谈责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。

    此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。



18、命令模式(Command)

    命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。



19、备忘录模式(Memento)

    主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。



20、状态模式(State)

    核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以, 状态模式就两点:

    1、可以通过改变状态来获得不同的行为。

    2、你的好友能同时看到你的变化。



21、访问者模式(Visitor)

    访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。—— From 百科

    简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。


    该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:

            1、新功能会不会与现有功能出现兼容性问题?

            2、以后会不会再需要添加?

            3、如果类不允许修改代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦,



22、中介者模式(Mediator)

    中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。

    如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。

    User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!


23、解释器模式(Interpreter)

一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。

Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现。


详细参考:http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html



注意:本文归作者所有,未经作者允许,不得转载