Bootstrap

设计模式

【本文转载自http://student.csdn.net/space.php?uid=95941&do=blog&id=30133
一、设计模式的概念
设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述。设计模式是针对问题和解的抽象,是对一系列具有共同的问题的完整的解决方案。设计模式强调系统的复用性,它帮助人们做出有利于系统复用的选择,从而避免设计损害系统的复用性,是可复用面向对象软件的基础。一个完整的设计模式主要由模式名称、问题、解决方案和效果四个基本要素组成。
设计模式中最重要的思想就是“封装变化的概念”。这是绝大多数射进模式的核心概念。一方面,设计出来的软件应该体现出一定的灵活性,以适应可能的变化;另一方面,必须把这种灵活性所带来的软件内部的复杂性封装起来,为外界提供一个简单而稳定的访问接口,这两方面分别从内部实现和外部接口上提高了软件的复用性。这就要求我们在进行可复用面向对象软件设计时应遵循:不断剥离出系统中可变化的部分并把这部分封装起来。
 
二、设计模式将带来什么
1、设计模式带来了一套通用的设计词汇
各种设计模式的名字组成了一个词汇表,这个词汇表可以帮助开发人员更好地交流。设计模式为设计者们交流讨论、书写文档以及探索各种不同设计提供了一套通用的设计词汇。设计模式使设计者可以在比设计表示或编程语言更高的抽象级别上谈论一个系统,从而降低了其复杂度。
2、书写文档和学习的辅助手段
当一个系统的文档描述系统所用的模式时,它们可以帮助人们更快地理解这个系统。学习这些设计模式有助于设计者理解已有的面向对象系统,同时也能提高设计者的设计水平。而且,按照一个系统所使用的设计模式来描述该系统可以使他人理解起来容易得多,否则,就必须对该系统的设计进行逆向工程来弄清其使用的设计模式。有一套通用的设计词汇的好处是你不必描述整个设计模式,而只要使用它的名字,当他人读到这个名字就会理解系统的设计。
3、设计模式是现有方法的一种补充
设计模式总结了专家的经验,使得普通的开发人员可以使用。面向对象方法可用来促进良好的设计,教新手如何设计,以及对设计活动进行标准化。设计模式是面向对象设计方法所缺少的一块重要内容。这些设计模式展示了如何使用诸如对象、继承和多态等基本技术。它们也展示了如何以算法、行为、状态或者需生成的对象类型将一个系统参数化。设计模式在将一个分析模型转换为一个实现模型时特别有用。一个成熟的设计方法不仅要有设计模式,还可有其他类型的模式,如分析模式,用户界面设计模式或者性能调节模式等等,但设计模式是最主要的部分。
4、设计模式为系统重构提供了目标
无论系统是否用脑海中的模式来设计,它们都可以使更改系统机构变得容易。开发可复用软件的一个问题是开发者常常不得不重新组织或重构软件系统。设计模式可以帮助开发者重新组织一个设计,同时还能减少以后的重构工作。设计模式记录了许多重构产生的设计结构。在设计初期使用这些模式可以防止以后的重构。而且即使是在系统建成以后才了解使用这些模式,它们仍可以教设计者如何修改系统。
 
三、三种设计模式的介绍
在用设计模式进行面向对象设计时,特别强调两个面向对象设计的原则:针对接口编程而不是针对实现编程和优先使用组合而不是类继承。其中针对接口编程而不是针对实现编程要求不将变量声明为某个特定的具体类,而是让它遵从抽象类所定义的接口。然而,有时不得不在系统的某个地方实例化具体的类,这时创建型模式(Factory Method,Abstract Factory,Builder,Prototype,Singleton)可以做到这点。在这里主要通过具体的实例介绍三种创建型设计模式:工厂方法模式(Factory Method),抽象工厂模式(Abstract Factory),以及生成器模式(Builder)。
假设有这样的一个企业:该企业有四川和重庆两个分公司,每个子公司都有一套自己的薪资计算规则,每个月每个公司都要计算自己公司的薪资。那么应该如何为该企业设计一套薪资系统呢?
我们可能这样想,首先抽象出一个薪资类,然后所有的子公司的薪资类都继承这个抽象的薪资类,最后在客户端判断是哪个子公司,然后新建该子公司的薪资类对象。如果是这样,那么当这个企业的子公司有许多的时候,客户端的条件判断岂不是要写许多,代码就会变得很长。
我们可以再创建一个类,把具体的判断语句抽取到这个类中,在客户端只负责薪资类的计算,这样可以解决在客户端要写许多判断语句的问题。还有一个问题就是如果该企业的子公司有很多,那么判断语句中的if…else语句就会很多,代码就不好看,而且一般在薪资计算前,都会有一些初始化工作要做,把许多公司的初始化工作都放在一个类中来做,代码量就可想而知了。所以,在这里,我们需要使用工厂方法模式来进行设计。
1、工厂方法模式(Factory Method)
(1)意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
(2)适用性:
1)当一个类不知道它所必须创建的对象的类的时候;
2)当一个类希望由它的子类来指定它所创建的对象的时候;
3)当类将创建对象的职责委托给多个帮助子类中的某一个,并且希望将哪一个帮助子类是代理者这一信息局部化的时候。
(3)参与者:
1)Product——定义工厂方法所创建的对象的接口
2)ConcreteProduct——实现Product接口
3)Creator——声明工厂方法,该方法返回一个Product类型的对象
4)ConcreteCreator——重定义工厂方法以创建一个Product对象。
(4)实例代码:
这里我们还是以上面的那个企业的薪资系统为例。代码如下:
//*** 抽象的薪资类定义 ***
//********Salary.java**********
interface Salary{
    void computerSalary();
}
 
//*** 四川子公司的薪资类的代码 *****
//****SCSalary.java******
class SCSalary implements Salary{
    public void computerSalary(){
       System. out .println( " 开始计算四川子公司的薪资 " );
    }
}
 
//*** 重庆子公司的薪资类的代码 *****
//****CQSalary.java*****
class CQSalary implements Salary{
    public void computerSalary(){
       System. out .println( " 开始计算重庆子公司的薪资 " );
    }
}
 
//*** 创建一个只负责定义创建方式的抽象的工厂类 ***
//***Factory.java*******
interface Factory{
    public Salary createSalary();
}
 
//*** 四川子公司的工厂类 ***
//***SCSalaryFactory.java***
class SCSalaryFactory implements Factory{
    public Salary createSalary(){
       return new SCSalary();
    }
}
 
//*** 重庆子公司的工厂类 ***
//***CQSalaryFactory.java***
class CQSalaryFactory implements Factory{
    public Salary createSalary(){
       return new CQSalary();
    }
}
 
//*** 客户端代码 ***
//***Client.java***
public class Client{
    public static void main(String [] args){
       Factory factory= new SCSalaryFactory();
       Salary salary=factory.createSalary();
       salary.computerSalary();
       factory= new CQSalaryFactory();
       salary=factory.createSalary();
       salary.computerSalary();
    }
}
 
在实例中,创建了一个只负责定义薪资创建方式的抽象工厂类 Factory ,而在客户端就只负责对象的调用即可。从而避免了把对象的创建和组装都放在客户端,造成客户端代码很复杂,同时也明确了各个类的职责。
(5 )小结:
在工厂方法模式中,客户端不需要负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责对象的调用,从而明确了各个类的职责。如果有新的产品加进来,只需要新增加一个具体的产品工厂类( ConcreteCreator )和具体的产品类 (ConcreteProduct) 就可以了,不会影响到原来已有的其他代码,代码量不会变大,后期维护更加容易,增强了系统的可扩展性。
在工厂方法模式中,一个具体的工厂类负责创建一个单独的产品,如果有两个不同的产品要创建,就需要两个不同的工厂类,即使这两个产品之间存在相关性,同样还需要两个不同的工厂类。同样如此,对于上面的实例,该企业计算完薪资,如果还要计算税收,倘若采用工厂方法模式,那么就需要定义两个抽象工厂类,分别用来定义薪资和税收,然后每个子公司分别继承这两个抽象工厂类以实现具体的工厂类,这样一来,每个子公司至少要 2 个工厂类:薪资、税收。如果该企业的子公司有许多,工厂类的数量就可想而知了。这时我们可以重新定义一个抽象的工厂类,用来定义创建薪资类和税收类的方式,这就是抽象工厂模式。
2 、抽象工厂模式 (Abstract Factory)
1 )意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
2 )适用性:
1 )一个系统要独立于它的产品的创建、组合和表示时;
2 )一个系统要由多个产品系列中的一个来配置时;
3 )当你要强调一系列相关的产品对象的设计以便进行联合使用时;
4 )当你提供一个产品类库,而只想显示它们的接口而不是实现时。
(3)参与者:
1)AbstractFactory——声明一个创建抽象产品对象的操作接口
2)ConcreteFactory——实现创建具体产品对象的操作
3)AbstractProduct——为一类产品对象声明一个接口
4)ConcreteProduct——实现AbstractProduct,定义一个将被相应的具体工厂创建的产品对象
5)Client——仅使用由AbstractFactory和AbstractProduct类声明的接口。
(4)实例代码:
税收类和薪资类的设计一样,先设计一个抽象的接口,然后每个子公司的税收类都继承该接口。代码如下:
//*** 抽象的薪资类定义 ***
//********Salary.java**********
interface Salary{
    void computerSalary();
}
 
//*** 四川子公司的薪资类的代码 *****
//****SCSalary.java******
class SCSalary implements Salary{
    public void computerSalary(){
       System. out .println( " 开始计算四川子公司的薪资 " );
    }
}
 
//*** 重庆子公司的薪资类的代码 *****
//****CQSalary.java*****
class CQSalary implements Salary{
    public void computerSalary(){
       System. out .println( " 开始计算重庆子公司的薪资 " );
    }
}
 
//*** 抽象的税收类定义
//***Tax.java***
interface Tax{
    void computerTax();
}
 
//*** 四川子公司的税收类代码 ***
//***SCTax.java***
class SCTax implements Tax{
    public void computerTax(){
       System. out .println( " 开始计算四川公司的税收 " );
    }
}
 
//*** 重庆子公司的税收类代码 ***
//***CQTax.java***
class CQTax implements Tax{
    public void computerTax(){
       System. out .println( " 开始计算重庆公司的税收 " );
    }
}
 
//*** 定义一个抽象的工厂类,用来定义创建薪资类和税收类的方式 ***
//***Factory.java*******
interface Factory{
    Salary createSalary();
    Tax createTax();
}
 
//*** 四川子公司的抽象工厂类 ***
//***SCFactory.java***
class SCFactory implements Factory{
    /* 创建四川子公司的薪资对象 */
    public Salary createSalary(){
       return new SCSalary();
    }
    /* 创建四川子公司的薪资对象 */
    public Tax createTax(){
       return new SCTax();
    }
}
 
//*** 重庆子公司的抽象工厂类 ***
//***CQFactory.java***
class CQFactory implements Factory{
    /* 创建重庆子公司的薪资对象 */
    public Salary createSalary(){
       return new CQSalary();
    }
    /* 创建重庆子公司的薪资对象 */
    public Tax createTax(){
       return new CQTax();
    }
}
 
//*** 客户端代码 ***
//***Client.java***
public class Client{
    public static void main(String [] args){
       Factory factory= new SCFactory();
       Salary salary=factory.createSalary();
       salary.computerSalary();
       Tax tax=factory.createTax();
       tax.computerTax();
       factory= new CQFactory();
       salary=factory.createSalary();
       salary.computerSalary();
       tax=factory.createTax();
       tax.computerTax();
    }
}
 
在这个实例中,创建了一个抽象工厂类 Factory 来定义薪资类和税收类的创建,而不是分别定义两个抽象的工厂类,分别负责定义薪资类和税收类的创建,从而避免了程序中出现许多工厂类,增加程序代码复杂度。
(5 )小结:
与工厂方法模式相同,在抽象工厂模式中,客户端不再负责对象的创建,而是把这个责任交给了具体的工厂类,客户端只负责对对象的调用,从而明确了各个类的职责。当一系列相互关联的产品被设计到一个工厂类里后,客户端的调用将会变得非常简单,而且,如果要更换这一系列的产品,只需要更换一个工厂类即可。但是如果有新的产品加进来,则需要修改抽象工厂类的设计,并同时修改实现这个抽象工厂类的具体工厂类,需要额外编写代码,增加了工作量。
在抽象工厂模式中,把相互关联的产品的对象的组装都放在了客户端,如果产品很多,客户端就会变得非常臃肿;但如果放在工厂类中,则又违反了单一职责的原则,使得工厂类既要负责对象的创建,又要负责对象的组装。当然,我们可以创建一个组装类来负责对象的组装,工厂类只负责对象的创建,而客户端直接调用组装类就可以了,这就是生成器模式。
3 、生成器模式 (Bulider)
1 )意图:将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。
2 )适用性:
1 )当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装方式时
2 )当构造过程必须允许被构造的对象有不同的表示时。
(3)参与者:
1)Builder——为创建一个Product对象的各个部件指定抽象接口
2)ConcreteBuilder——实现Builder的接口以构造和装配该产品的各个部件
3)Director——构造一个使用Builder接口的对象
4)Product——表示被构造的复杂对象,包含定义组成部件的类。
(4)实例代码:
//*** 抽象的薪资类定义 ***
//********Salary.java**********
interface Salary{
    void computerSalary();
}
 
//*** 四川子公司的薪资类的代码 *****
//****SCSalary.java******
class SCSalary implements Salary{
    public void computerSalary(){
       System. out .println( " 开始计算四川子公司的薪资 " );
    }
}
 
//*** 重庆子公司的薪资类的代码 *****
//****CQSalary.java*****
class CQSalary implements Salary{
    public void computerSalary(){
       System. out .println( " 开始计算重庆子公司的薪资 " );
    }
}
 
//*** 抽象的税收类定义
//***Tax.java***
interface Tax{
    void computerTax();
}
 
//*** 四川子公司的税收类代码 ***
//***SCTax.java***
class SCTax implements Tax{
    public void computerTax(){
       System. out .println( " 开始计算四川公司的税收 " );
    }
}
 
//*** 重庆子公司的税收类代码 ***
//***CQTax.java***
class CQTax implements Tax{
    public void computerTax(){
       System. out .println( " 开始计算重庆公司的税收 " );
    }
}
 
//*** 定义一个抽象的工厂类,用来定义创建薪资类和税收类的方式 ***
//***Factory.java*******
interface Factory{
    Salary createSalary();
    Tax createTax();
}
 
//*** 四川子公司的抽象工厂类 ***
//***SCFactory.java***
class SCFactory implements Factory{
    /* 创建四川子公司的薪资对象 */
    public Salary createSalary(){
       return new SCSalary();
    }
    /* 创建四川子公司的薪资对象 */
    public Tax createTax(){
       return new SCTax();
    }
}
 
//*** 重庆子公司的抽象工厂类 ***
//***CQFactory.java***
class CQFactory implements Factory{
    /* 创建重庆子公司的薪资对象 */
    public Salary createSalary(){
       return new CQSalary();
    }
    /* 创建重庆子公司的薪资对象 */
    public Tax createTax(){
       return new CQTax();
    }
}
 
//*** 组装类,负责产品的组装 ***
//***Director.java***
class Director{
    private Factory factory ;
    public Director(Factory factory){
        this . factory =factory;
    }
   
    public void computer(){
       Salary salary= factory .createSalary();
       salary.computerSalary();
       Tax tax= factory .createTax();
       tax.computerTax();
    }
}
//*** 客户端代码 ***
//***Client.java***
public class Client{
    public static void main(String [] args){
       Director director= new Director( new SCFactory());
       director.computer();
    }
}
 
在这里,我们创建了一个工厂类 Factory 负责定义薪资类和税收类的创建,并且创建了一个创建者类 Director 负责对象的组装,而在客户端则只需要调用这个创建者类即可,从而明确了各个类的职责,使得各个类更好的分工。
(5 )小结:
在构造器模式中,客户端不再负责对象的创建和组装,而是把这个创建的责任交给了具体的创建者类,把组装的责任交给组装类,客户端只负责对象的调用,从而明确了各个类的职责。
虽然利用构造器模式可以创建出不同类型的产品,但如果产品之间的差异非常大,就需要编写多个创建这类才能实现,这是如果结合工厂模式更好。
;