设计模式——简介
去年的时候在学校上课时,学习了设计模式,最近做开发的时候发现有些模式真的是很好用,对程序的维护特别方便。所以,今个开始,我打算重新再看一遍23种设计模式,然后和大家一起分享分享。
注:此篇文章参考自Java设计模式(耿祥义 张跃平 著,清华大学出版社.2013)。
一、首先,了解下什么是设计模式吧。
我们知道,对于一套软件而言,设计者都会让它尽可能满足以下几个目标:
1、正确性:也就是满足应用程序的需求。
2、健壮性: 是指软件对于规范要求以外的输入情况的处理能力。 也就是说, 在异常情况下 ,软件能够正常运行的能力。
3、灵活性:就是可以允许代码修改平稳地发生,而不会波及到很多其他的模块。
4、可重用性:也就是重复使用的意思。
5、高效性:一般指两个方面,一是执行效率,二是存储效率。
而评判一个软件设计的良好往往是通过以下三个方面:
1、可扩展性:新功能容易加入,而且不会影响已有功能,即不“僵硬”。
2、灵活性:修改一个地方,不会影响其他,即不“脆弱”。
3、可插入性:用一个容易替换另一个类,只要它们实现相同接口即可,即低“黏度”。
而设计模式就是为了达到上述的设计理论而生的,一个设计模式是针对某一类问题的最佳解决方案,而且已经被成功应用于许多系统的设计中,它解决了在某种特定情景中重复发生的某个问题,因此,可以这样定义设计模式:设计模式是从许多优秀的软件系统中总结出成功的可复用的设计方案。
记录一个设计模式需要四个基本要素:
1、名称:一个模式的名称高度概括该模式的本质,有利于该行业统一术语、便于交流使用。
2、问题:描述应该在何时使用模式,解释设计问题和问题存在的前因后果,描述在怎样的环境下使用模式。
3、方案:描述设计的组成部分、它们之间的相互关系及各自的职责和协作方式。
4、效果:描述模式的应用效果及使用模式应当权衡的问题。主要效果包括使用模式对系统的灵活性、扩充性和复用性的影响。
例如:中介者模式:
1、名称:中介者。
2、问题:用一个中介者来封装一系列的对象交互。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
3、方案:中介者接口(Mediator),具体中介者(ConcreteMediator)、同事(Colleague)、具体同事(ConcreteColleague)。
4、效果:减少了子类的生成,将各个同事解耦,简化了对象协议,控制集中化。
二、“23个设计模式”的来源
目前,被公认在设计模式领域最具影响力著作是Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides在1994年合作出版的著作《Design Patterns:Elements of Reusable Object-Oriented Software》(中译本《设计模式:可复用的面向对象软件的基本原理》),该书的四位作者在其著作中记录了他们在四年多的工作中所发现的23个设计模式。《设计模式》一书被广大喜爱者昵称为GOF(Gang of Four)之书(*之书),GOF之书已经被公认为师设计模式领域的奠基之作。
三、设计原则
软件设计原则:单一职责原则、开闭原则、里氏替换原则、接口分离原则、依赖倒置原则。具体如下:
1、单一职责原则(SRP):一个类应该有且只有一个改变的理由,它要求“一个设计元素只做一件事”。
2、开闭原则(OCP):不修改原有类就能扩展一个类的行为。也就是说,一个软件实体应当对扩展开放,对修改关闭。
3、里氏替换原则(LSP):子类能替换其超类(is-a 关系) ,也就是说子类型(subtype)必须能替换其基类型(base type)。
4、接口分离原则(ISP):使用多个专门的接口比使用单一的总接口更好;换言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小的接口之上的; 不应该强迫客户程序依赖于它们不用的接口
5、依赖倒置原则(DIP):要依赖于抽象,不要依赖于具体:也就是说,抽象不应当依赖于细节,细节应当依赖于抽象;要针对接口编程,不要针对实现编程。
四、设计模式的分类
①依据设计模式的 行为方式 ,也即其目的,将设计模式分为三种类型:创建型模式、结构型模式、行为型模式:
创建型模式:以灵活的方式创建对象的集合,如:工厂模式、抽象工厂模式、建造者模式、单件模式、原型模式等。
结构型模式:代表相关对象的集合,如:适配器模式、装饰模式、桥接模式、享元模式 、外观模式、代理模式、组合模式。
行为型模式:在对象集合中捕获行为,如:模板方法模式、观察者模式、迭代子模式 、责任链模式、备忘录模式、命令模式、状态模式、访问者模式、中介者模式、策略模式。
②根据 设计意图可分为五类:接口型设计模式、责任型设计模式、构造型设计模式、操作型设计模式、扩展型设计模式。
五、设计模式引入——简单工厂模式(不属于23种设计模式之一)
1、模式动机
考虑一个简单的软件应用场景,一个购物系统可以提供多种方式的支付(如现金支付、银联支付、代金券支付等),这些支付都源自同一个基类 ,不过在继承基类后不同的子类修改了部分属性从而使得它们可以呈现不同的功能,如果我们希望在使用这些支付方式时, 不需要知道这些具体支付类的名字,只需要知道表示该支付类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的支付方式类对象。
2、模式定义
简单工厂模式 (Simple Factory Pattern) :又称为静态工厂方法 (Static Factory Method) 模式 ,它属于类创建型模式。在简单工厂模式中,可以 根据参数的不同返回不同类的实例 。简单工厂模式 专门定义一个类来负责创建其他类的实例 , 被创建的实例通常都具有共同的父类 。
3、模式结构
简单工厂模式包含如下角色:
• Factory :工厂角色
• Product :抽象产品角色
• ConcreteProduct :具体产品角色
4、不使用简单工厂模式模拟支付的代码
public void pay(String type) { if(type.equalsIgnoreCase("cash")){ //现金支付处理代码 } else if(type.equalsIgnoreCase("creditcard")){ //信用卡支付处理代码 }else if(type.equalsIgnoreCase("voucher")){ //代金券支付处理代码 }else{ …… } }
可以发现,一当我有新的支付方式添加进来,如支付宝支付、微信支付等,我们就需要改动这个方法的代码,这样严重不符合我们“开闭原则”,对代码的维护很难。
5、使用简单工厂模式增强其可维护性
还是这个问题,我们来看看使用简单工厂模式后的代码吧。
(1)首先,根据简单工厂模式的结构,需要定义一个支付方式的共同抽象基类(接口)——Pay.java,里面只包含一个支付的方法。
public interface Pay { public void pay(); }
(2)当我们需要添加一种具体的支付方式的时候,如现金支付,我们可以让其继承(实现)于Pay——CashPay.java
public class CashPay implements Pay { public void pay() { //模拟处理现金支付的代码 System.out.println("This is CashPay!"); } }
(3)创建一个支付方式工厂类,用于生产这些支付方式——PayFactory.java
public class PayFactory { public Pay getPayMethod(String type) { if(type.equals("Cash")) return new CashPay(); return null; } }
可以看到,当我们传入参数“cash”的时候,就会返回一个具体的支付方式类——CashPay。
(4)最后,我们来看看对于客户端代码(对应第4部分的代码对比),应该怎么写.。
public class Test { public static void main(String args[]) { // 创建一个支付方式工厂类对象 PayFactory pf = new PayFactory(); // 调用工厂的方法,传入参数,获取对应的具体支付类 Pay p = pf.getPayMethod("cash"); // 通过父类对象指向具体子类的引用来调用具体的子类方法 p.pay(); } }
通过与第4点中不使用设计模式来对比,我们可以发现,当我们新增一种支付方式的时候,如信用卡支付,此时,只需要,增加一个类CreditPay让其继承于Pay,并重写其pay()方法,然后只需修改工厂类中的代码,增加if语句返回对应的对象即可。而客户端只需根据用户选择动态生成字符串参数即可。可以发现代码的维护性大大提高了。
谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~
上一篇: Centos7.2配置双网卡
下一篇: 简单工厂设计模式