一、前言
首先要说的就是设计模式的定义,我记得这是上学期《软件设计模式》考试中的第一道名词解释题,当时为了应付考试而背得滚瓜烂熟,可是这个学期一转眼就忘记了。软件模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。这是上学期《软件设计模式》课程教材里的定义,精炼简洁的高度概括了软件设计模式的内涵和作用。我们这个学期主要学习软件体系结构,提到的最多的概念就是软件架构,可以说软件设计模式和软件架构有一些共通之处,实际上软件设计模式和软件架构都是一种模式,都是为了避免总是新发明一种方案来解决实际问题,对某些有着共同特性的问题和解决方案可以进行一定程度的抽象化,提炼出来的抽象化公共要素就是模式。但是软件架构和软件设计模式又切不可混为一谈。软件架构是从宏观上谈的,主要关注软件系统的各个层次结构,而软件设计模式则在微观上,在代码层次上对某一处细节进行处理,对某些相关的java类进行模式化的处理。
二、设计模式的原则
因为应用设计模式的目的就是提高代码的可复用性,所以为了实现这个目的GoF设计模式就有一些原则。第一条是开闭原则,开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。有的人会说设计模式会导致类的数量上升,貌似是把问题复杂化了,但是我们不能只看到这个表表象,我们一个认识到使用抽象类和接口能大大提高软件的可修改性,而这正是软件质量属性中很重要的一方面。第二条是里氏代换原则,这也是软件设计模式中的基本原则之一。这个原则是说任何基类可以出现的地方,子类一定可以出现。这条原则是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。第三条是依赖倒转原则,这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。第四条是接口隔离原则,这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。第五条原则是迪米特法则,也被称为最少知道原则,是说一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。最后一条是合成复用原则,是指应该尽量使用合成/聚合的方式,而不是使用继承。
三、对设计模式的理解
1.工厂方法模式。凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
2.抽象工厂模式。为了遵守闭包原则,还可以对工厂类进行抽象,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。其实这个模式的好处就是,如果你现在想增加一个功能,则只需做一个实现类,实现接口,同时做一个工厂类,实现接口,就OK了,无需去改动现成的代码。这样做能实现较好的拓展性。
3.单例模式。在实际应用中我们常常对某个类只实例化一个对象,比如某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销,另外只是李华一个对象就省去了new操作符,降低了系统内存的使用频率。最重要的是有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统会出现紊乱,所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。
4、建造者模式。工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。
5、原型模式。原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。复制有两种类型,浅复制是将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。深复制是将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
6.适配器模式。适配器模式将某个类的接口转换成客户端期望的另一个接口表示。有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。
7、装饰模式。顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。当我们需要扩展一个类的功能,或者是动态的为一个对象增加功能,而且还能动态撤销的时候就可以使用这个模式。
8、代理模式。其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。比如如果已有的方法在使用的时候需要对原有的方法进行改进,可以采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
9、外观模式。外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,起到了解耦的作用。
10、桥接模式。桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
11、组合模式。组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,比如将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。
12、享元模式。享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。找一个类负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象。比如Java里面的JDBC连接池,通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能。
13、策略模式。策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
14、模板方法模式模板方法模式就是指一个抽象类中,有一个主方法,再定义n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。
15、观察者模式。观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化。对象之间是一种一对多的关系。
16、迭代子模式。迭代器模式就是顺序访问聚集中的对象。一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。
17、责任链模式。有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。链接上的请求可以是一条链,可以是一个树,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。
18、命令模式。比如司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。
19、备忘录模式。主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,其实就是一种备份模式。假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。
20、状态模式。核心思想就是当对象的状态改变时,同时改变其行为。比如账户余额,如果有钱状态就可以取钱,欠费或者没钱的时候就不能取钱。
21、访问者模式。访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。如果我们想为一个现有的类增加新功能,必须考虑新功能会不会与现有功能出现兼容性问题还有就是以后会不会再需要添加。最后还有如果类不允许修改代码怎么办。面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦,
22、中介者模式。中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。
23、解释器模式。解释器模式一般主要应用在编译器的开发中,所以适用面比较窄。