首页 > 代码库 > Java 下各种设计模式小结

Java 下各种设计模式小结

策略模式——定义算法族,分别封装起来,让它们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。
    策略模式是说,针对一种计算,定义一系列的算法,由用户决定详细使用哪一个算法完毕计算。

 

    比方,提供一个计算银行存款利率的接口,对于不同的存款方式(活期、一年定期、两年定期),提供不同的算法实现类,由用户决定使用哪种存款方式来计算利率。假设银行计算利率的算法发生了变化(如又添加了三年定期、五年定期的算法),对于用户的使用不产生不论什么影响,由于用户使用的是统一的计算接口,也符合了针对接口编程,不针对实现编程的设计原则。

 

定义一个计算存款利率的接口:

Java代码  收藏代码
  1. public interface IRateCalculator {  
  2.     public double calculate(double amount);  
  3. }  

 计算活期存款利率的实现类:

Java代码  收藏代码
  1. public class CurrentRateCalcalator implements IRateCalculator {  
  2.   
  3.     private double rate = 0.035;  
  4.       
  5.     /** 
  6.      * 计算活期存款利率 
  7.      */  
  8.     @Override  
  9.     public double calculate(double amount) {  
  10.         return amount * rate;  
  11.     }  
  12. }  

 计算一年定期存款利率的实现类:

Java代码  收藏代码
  1. public class OneYearRateCalculator implements IRateCalculator {  
  2.       
  3.     private double rate = 0.0325;  
  4.       
  5.     /** 
  6.      * 计算一年定期存款利率 
  7.      */  
  8.     @Override  
  9.     public double calculate(double amount) {  
  10.         return amount * rate;  
  11.     }  
  12. }  

 计算两年定期存款利率的实现类:

Java代码  收藏代码
  1. public class TwoYearRateCalculator implements IRateCalculator {  
  2.   
  3.     private double rate = 0.0375;  
  4.       
  5.     /** 
  6.      * 计算两年定期存款利率 
  7.      */  
  8.     @Override  
  9.     public double calculate(double amount) {  
  10.         return amount * rate;  
  11.     }  
  12. }  

 

測试类:

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 策略模式——定义算法族,分别封装起来,让它们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。 
  4.      *  
  5.      * 针对一种计算,定义一系列的算法,由用户决定详细使用哪一个算法完毕计算。 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         //如果有100块钱的本金  
  10.         double amount = 100;  
  11.         double rate;  
  12.         IRateCalculator rateCalculator;  
  13.           
  14.         //计算活期存款利率  
  15.         rateCalculator = new CurrentRateCalcalator();  
  16.         rate = rateCalculator.calculate(amount);  
  17.         System.out.println("活期存款利率为:" + rate);  
  18.           
  19.         //计算一年定期存款利率  
  20.         rateCalculator = new OneYearRateCalculator();  
  21.         rate = rateCalculator.calculate(amount);  
  22.         System.out.println("一年定期存款利率为:" + rate);  
  23.           
  24.         //计算两年定期存款利率  
  25.         rateCalculator = new TwoYearRateCalculator();  
  26.         rate = rateCalculator.calculate(amount);  
  27.         System.out.println("两年定期存款利率为:" + rate);  
  28.     }  
  29. }  

 

命令模式(Command Pattern)——将“请求”封装成对象,以便使用不同的请求、队列或者日志来參数化其它对象。命令模式也支持科撤销的操作。

    命令模式适用于“请求-响应”模式的功能,将用户的请求封装成对象(命令),用户须要运行什么样的操作,就调用什么样的命令,而无需知道命令的运行逻辑是什么。

    命令模式主要包括下面几个概念:

1、Command:全部命令的抽象类,一般须要对外公开一个运行命令的方法execute,如有须要还需提供一个命令的撤销方法undo。

2、ConcreteCommand:命令的实现类。

3、Invoker:调用者,负责命令的调度。

4、Reveiver:接收者,负责命令的接收和运行。

5、Client:client,命令的发起者。

 

    比方,一个有存取款功能的ATM机,它能够向某个银行的卡里存款,也能够从不论什么支持银联接口的银行卡里取款。不同的银行系统对存款、取款功能的实现均有不同,我们不关心银行怎么实现,仅仅要点击ATM上的button即可了。这个样例中,银行的存款和取款分别为两个详细的命令实现类(ConcreteCommand);ATM机充当调用者(Invoker),负责调用银行存款或取款的命令;银行的系统为接收者(Reveiver),处理存款和取款的业务逻辑;使用ATM机的人就是client。

 

    首先定义一个命令的抽象类,仅仅有两个方法,运行和撤销:

 

Java代码  收藏代码
  1. public interface Command {  
  2.     public void execute();  
  3.     public void undo();  
  4. }  

    模拟两个银行的系统,建行(CCB)和招行(CMB),简单一点,仅仅有存款和取款的功能。为了模拟银行系统实现的差异,将存款、取款方法取成不同的名字。

 

    建行:

 

Java代码  收藏代码
  1. public class Ccb {  
  2.     public void cunqian(long amount) {  
  3.         System.out.println("向建设银行存入金额:" + amount);  
  4.     }  
  5.   
  6.     public void quqian(long amount) {  
  7.         System.out.println("从建设银行取出金额:" + amount);  
  8.     }  
  9. }  

    招行:

Java代码  收藏代码
  1. public class Cmb{  
  2.     public void saveMoney(long amount) {  
  3.         System.out.println("向招商银行存入金额:" + amount);  
  4.     }  
  5.     public void getMoney(long amount) {  
  6.         System.out.println("从招商银行取出金额:" + amount);  
  7.     }  
  8. }  

    将银行的存款、取款动作封装成命令对象。为了避免太复杂,存款命令的撤销就当再取出来,取款命令的撤销就再存回去。

    建行的存款命令:

 

Java代码  收藏代码
  1. public class CcbDepositCommand implements Command {  
  2.     private Ccb ccb = new Ccb();  
  3.     @Override  
  4.     public void execute() {  
  5.         ccb.cunqian(100);  
  6.     }  
  7.     @Override  
  8.     public void undo() {  
  9.         ccb.quqian(100);  
  10.     }  
  11. }  

    建行的取款命令:

 

 

Java代码  收藏代码
  1. public class CcbWithdrawCommand implements Command {  
  2.     private Ccb ccb = new Ccb();  
  3.     @Override  
  4.     public void execute() {  
  5.         ccb.quqian(100);  
  6.     }  
  7.     @Override  
  8.     public void undo() {  
  9.         ccb.cunqian(100);  
  10.     }  
  11. }  

    招行的存款命令:

 

 

Java代码  收藏代码
  1. public class CmbDepositCommand implements Command {  
  2.     private Cmb cmb = new Cmb();  
  3.     @Override  
  4.     public void execute() {  
  5.         cmb.saveMoney(100);  
  6.     }  
  7.     @Override  
  8.     public void undo() {  
  9.         cmb.getMoney(100);  
  10.     }  
  11. }  

    招行的取款命令:

 

 

Java代码  收藏代码
  1. public class CmbWithdrawCommand implements Command {  
  2.     private Cmb cmb = new Cmb();  
  3.     @Override  
  4.     public void execute() {  
  5.         cmb.getMoney(100);  
  6.     }  
  7.     @Override  
  8.     public void undo() {  
  9.         cmb.saveMoney(100);  
  10.     }  
  11. }  

    ATM机刚出厂时可能还没设置不论什么命令,所以为每一个button预置一个什么都不做的命令:

Java代码  收藏代码
  1. public class NoCommand implements Command {  
  2.     @Override  
  3.     public void execute() { }  
  4.     @Override  
  5.     public void undo() { }  
  6. }  

    如今来实现一个刚出厂的ATM机,它可能完毕不论什么银行的存取款命令,这些命令由负责购买ATM机的银行日后自行规划:

Java代码  收藏代码
  1. public class Atm {  
  2.     private Command[] command;  
  3.       
  4.     public Atm(){  
  5.         this.command = new Command[]{new NoCommand()};  
  6.     }  
  7.     //设置一组要运行的命令  
  8.     public void setCommand(Command command[]) {  
  9.         this.command = command;  
  10.     }  
  11.     //运行命令的方法  
  12.     public void action(int i) {  
  13.         this.command[i].execute();  
  14.     }  
  15.     //撤销命令的方法  
  16.     public void cancel(int i) {  
  17.         this.command[i].undo();  
  18.     }  
  19. }  

  

    如今如果要实现一个建行的ATM机,仅仅有三个功能:1是向建行存款;2是从建行取款;3从招行取款。仅仅要为ATM设置上对应的命令就能够了。測试类:

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 命令模式——将“请求”封装成对象,以便使用不同的请求、队列或者日志来參数化其它对象。 
  4.      * 命令模式也支持科撤销的操作。 
  5.      *  
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         //调用者来运行命令  
  10.         Atm atm = new Atm();  
  11.         Command[] commands = new Command[3];  
  12.         commands[0] = new CcbDepositCommand();  
  13.         commands[1] = new CcbWithdrawCommand();  
  14.         commands[2] = new CmbWithdrawCommand();  
  15.         atm.setCommand(commands);  
  16.         atm.action(0);  
  17.         atm.cancel(0);  
  18.         atm.action(1);  
  19.         atm.cancel(1);  
  20.         atm.action(2);  
  21.         atm.cancel(2);  
  22.     }  
  23. }  

    当然,假设我们不适用ATM机,直接到银行的窗体,营业员也能够直接调用系统对应的命令:

Java代码  收藏代码
  1. //直接运行详细命令  
  2. Command command = new CcbDepositCommand();  
  3. command.execute();  
  4. command.undo();  

 

    命令模式的扩展性、封装性非常好,能够非常好的将用户请求与请求的实现解耦,对需求的变化也更easy扩展。但一个非常easy的请求都须要封装为一个命令,也会导致类的膨胀,因此开发时需依据实际须要推断是否使用命令模式。


模版方法模式(Template Method Pattern)——定义一个操作中算法的框架,而将一些步骤延迟到子类中。使得子类能够不改变一个算法的结构就可以重定义该算法的某些特定步骤。

 

    模版方法模式适用于一组固定流程的算法,在抽象类中定义一组算法,由子类去实现,抽象类提供一个公开方法,确定调用这组算法的步骤。

 

    比方,我们去营业厅办理一张手机卡,不论是移动、联通还是电信,流程都是先办卡、再选号,而办卡和选号的动作每一个运营商自己去实现。

    我们定义一个运营商的抽象类,有两个抽象的方法办卡和选号,另一个方法就是提供服务,调用办卡和选号:

 

Java代码  收藏代码
  1. public abstract class ServiceOperator {  
  2.     protected abstract void requestCard();  
  3.     protected abstract void selectNumber();  
  4.       
  5.     final public void service(){  
  6.         this.requestCard();//先办卡  
  7.         this.selectNumber();//再选号  
  8.     }  
  9. }  

     分别定义两个实现类,移动和联通:

 

 

Java代码  收藏代码
  1. public class ChinaMobile extends ServiceOperator {  
  2.     @Override  
  3.     protected void requestCard() {  
  4.         System.out.println("办一张中国移动电话卡");  
  5.     }  
  6.     @Override  
  7.     protected void selectNumber() {  
  8.         System.out.println("选一个中国移动电话号");  
  9.     }  
  10. }  

 

Java代码  收藏代码
  1. public class ChinaUnicom extends ServiceOperator {  
  2.     @Override  
  3.     protected void requestCard() {  
  4.         System.out.println("办一张中国联通电话卡");  
  5.     }  
  6.     @Override  
  7.     protected void selectNumber() {  
  8.         System.out.println("选一个中国联通电话号");  
  9.     }  
  10. }  

     这样,不论哪个运营商要改动办卡或是选号的操作时,对我们的整个流程不会产生不论什么影响了。对于办卡和选号这两个动作,称之为基本方法,由详细的实现类去完毕;提供服务的方法称之为模版方法,定义了基本方法的调用流程。

 

 

測试类:

 

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 模版方法模式(Template Method Pattern)——定义一个操作中算法的框架,而将一些步骤延迟到子类中。 
  4.      * 使得子类能够不改变一个算法的结构就可以重定义该算法的某些特定步骤。 
  5.      *  
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         ServiceOperator serviceOperator = new ChinaUnicom();  
  10.         serviceOperator.service();  
  11.     }  
  12. }  

 

 

    模版方法模式还能够进一步进行扩展。对于某些基本方法的调用与否,可能须要一些条件,这时,我们能够在模版抽象类中定义一个控制这些条件的方法,称之为钩子方法(Hood Method),由子类去设定这个条件,以使不同的实现类能够做出不同的处理。

    比方,手机卡丢了,要去营业厅补一张,移动说,丢了没办法,重办卡、重选号吧,联通为了抢客户,就说,办张卡,号还用原来那个,不用选了。为了实现这个逻辑,我们在运营商的抽象类中添加一个推断是否为新用户的方法,假设是新用户,就不调用选号的方法了:

Java代码  收藏代码
  1. public abstract class ServiceOperator {  
  2.     protected abstract void requestCard();//基本方法  
  3.     protected abstract void selectNumber();//基本方法  
  4.       
  5.     //模版方法  
  6.     final public void service(){  
  7.         this.requestCard();//先办卡  
  8.         if(this.isNewCustomer()){  
  9.             this.selectNumber();//再选号  
  10.         }  
  11.     }  
  12.       
  13.     //钩子方法(Hood Method)  
  14.     protected boolean isNewCustomer(){  
  15.         return true;  
  16.     }  
  17. }  

    移动无论他是新用户还是老用户,全当新用户处理:

Java代码  收藏代码
  1. public class ChinaMobile extends ServiceOperator {  
  2.     @Override  
  3.     protected void requestCard() {  
  4.         System.out.println("办一张中国移动电话卡");  
  5.     }  
  6.     @Override  
  7.     protected void selectNumber() {  
  8.         System.out.println("选一个中国移动电话号");  
  9.     }  
  10.     @Override  
  11.     protected boolean isNewCustomer() {  
  12.         return true;  
  13.     }  
  14. }  

    联通让用户自己决定,老用户就能够不选号,光办卡:

Java代码  收藏代码
  1. public class ChinaUnicom extends ServiceOperator {  
  2.     private boolean isNewCustomer = true;  
  3.     @Override  
  4.     protected void requestCard() {  
  5.         System.out.println("办一张中国联通电话卡");  
  6.     }  
  7.     @Override  
  8.     protected void selectNumber() {  
  9.         System.out.println("选一个中国联通电话号");  
  10.     }  
  11.     @Override  
  12.     protected boolean isNewCustomer() {  
  13.         return this.isNewCustomer;  
  14.     }  
  15.     public void setNewCustomer(boolean isNewCustomer) {  
  16.         this.isNewCustomer = isNewCustomer;  
  17.     }  
  18. }  

測试类:

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 模版方法模式(Template Method Pattern)——定义一个操作中算法的框架,而将一些步骤延迟到子类中。 
  4.      * 使得子类能够不改变一个算法的结构就可以重定义该算法的某些特定步骤。 
  5.      *  
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         ChinaUnicom chinaUnicom = new ChinaUnicom();  
  10.         chinaUnicom.setNewCustomer(false);  
  11.         chinaUnicom.service();  
  12.     }  
  13. }  

 

单例模式——确保一个类仅仅有一个实例,并提供一个全局訪问点。

 

    单例模式一般分为懒汉式和恶汉式,懒汉式是说当第一次获取类时才进行类的实例化,饿汉式是说当类被载入时直接实例化。定义单例模式的一般步骤是:

 * 定义一个私有的构造函数,以保证这个类不能被外部程序实例化;
 * 定义一个类的实例变量,以保存这个类的唯一实例;
 * 定义一个获取类唯一实例的静态方法,使外部程序能够获取这个类的唯一实例。

 

懒汉式:

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private static Singleton uniqueInstance;  
  3.    
  4.     private Singleton() {}  
  5.    
  6.     public static synchronized Singleton getInstance() {  
  7.         if (uniqueInstance == null) {  
  8.             uniqueInstance = new Singleton();  
  9.         }  
  10.         return uniqueInstance;  
  11.     }  
  12. }  

    使用synchronizedkeyword保证获取实例时,假设实例为null,仅仅有一个线程去创建该实例,但这样做会导致效率低下,以下有更好的解决的方法

 

线程安全的懒汉式:

 

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private volatile static Singleton uniqueInstance;  
  3.   
  4.     private Singleton() {}  
  5.   
  6.     public static Singleton getInstance() {  
  7.         if (uniqueInstance == null) {  
  8.             synchronized (Singleton.class) {  
  9.                 if (uniqueInstance == null) {  
  10.                     uniqueInstance = new Singleton();  
  11.                 }  
  12.             }  
  13.         }  
  14.         return uniqueInstance;  
  15.     }  
  16. }  

    用“双重检查加锁”,在getInstance中降低使用同步。volatilekeyword确保,当uniqueInstance变量被初始化成 Singleton实例时,多个线程正确的处理uniqueInstance变量。注意,1.4及更早的Java中,很多JVM对于volatile关键 字的实现会导致双重加锁失效。

 

 

饿汉式:

Java代码  收藏代码
  1. public class Singleton {  
  2.     private static Singleton uniqueInstance = new Singleton();  
  3.    
  4.     private Singleton() {}  
  5.    
  6.     public static Singleton getInstance() {  
  7.         return uniqueInstance;  
  8.     }  
  9. }  

    饿汉式在类被载入时直接实例化,因此不存在获取实例时的线程安全问题。


工厂模式(Factory Pattern)

    工厂模式,顾名思义,实际上就是定义可以生成对象的工厂,对于某种对象,事实上例化过程由对象工厂来完毕。
    工厂模式可细分为简单工厂模式(Simple Factory Pattern)、工厂方法模式(Factory Method Pattern)和抽象工厂模式(Abstract Factory Pattern)。
简单工厂模式(Simple Factory Pattern)——又称静态工厂方法模式(Static Factory Method Pattern),非常easy,就是通过一个工厂类来负责对象的创建。

比 如,你要办一张联通的电话卡,联通有A计划套餐、B计划套餐,无论什么套餐,都能打电话,你仅仅要到营业厅买一张卡即可了。当中,电话卡是一个接口 (SimCard),它有一个方法就是用来打电话(service),A计划和B计划的卡都是事实上现类(SimCardPlanA、 SimCardPlanB),营业厅就是一个工厂,用来生产电话卡(ChinaUnicomFactory),你仅仅要告诉它你要什么套餐即可了。

 

电话卡接口(SimCard):

Java代码  收藏代码
  1. public interface SimCard {  
  2.     public void service();  
  3. }  

 A套餐的卡(SimCardPlanA):

Java代码  收藏代码
  1. public class SimCardPlanA implements SimCard {  
  2.     @Override  
  3.     public void service() {  
  4.         System.out.println("联通A计划电话卡");  
  5.     }  
  6. }  

 B套餐的卡(SimCardPlanB):

Java代码  收藏代码
  1. public class SimCardPlanB implements SimCard {  
  2.     @Override  
  3.     public void service() {  
  4.         System.out.println("联通B计划电话卡");  
  5.     }  
  6. }  

 生产电话卡的工厂,也就是营业厅(ChinaUnicomFactory):

Java代码  收藏代码
  1. public class ChinaUnicomFactory {  
  2.     public static SimCard getSimCard(String kind) {  
  3.         if("PlanA".equals(kind)){  
  4.             return new SimCardPlanA();  
  5.         }else if("PlanB".equals(kind)){  
  6.             return new SimCardPlanB();  
  7.         }else{  
  8.             System.out.println("中国联通尚未推出该套餐");  
  9.             return null;  
  10.         }  
  11.     }  
  12. }  

 

測试类:

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 又称静态工厂方法模式(Static Factory Method Pattern),非常easy, 
  4.      * 就是通过一个工厂类来负责对象的创建。 
  5.      *  
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         //获取一个联通A计划的电话卡  
  10.         SimCard simCard1 = ChinaUnicomFactory.getSimCard("PlanA");  
  11.         simCard1.service();  
  12.           
  13.         //获取一个联通B计划的电话卡  
  14.         SimCard simCard2 = ChinaUnicomFactory.getSimCard("PlanB");  
  15.         simCard2.service();  
  16.     }  
  17. }  

 

 

工厂方法模式(Factory Method Pattern)——定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

继 续上面的样例,能生产电话卡的不光是联通啊,移动、电信也行啊,仅仅要是运营商都卖手机卡啊。于是,我们将上面的工厂改造一下,提供一个运营商工厂的接口 (ServiceOperatorFactory),联通工厂(ChinaUnicomFactory)、移动工厂 (ChinaMobileFactory)都实现了运营商接口的实现类,再添加两种卡的类型,移动的全球通卡(SimCardQQT)和神州行卡 (SimCardSZX)。这样,当用户想办联通卡时就用联通的工厂,想用移动卡时就用移动的工厂,没准哪天都不想用了,想换个电信的,没问题,不用改动 写好的代码,再加一个电信的工厂即可了,扩展性得到提高了吧。

 

运营商接口(ServiceOperatorFactory):

Java代码  收藏代码
  1. public interface ServiceOperatorFactory {  
  2.     public SimCard getSimCard(String kind);  
  3. }  

 联通工厂(ChinaUnicomFactory):

Java代码  收藏代码
  1. public class ChinaUnicomFactory implements ServiceOperatorFactory {  
  2.     @Override  
  3.     public SimCard getSimCard(String kind) {  
  4.         if("PlanA".equals(kind)){  
  5.             return new SimCardPlanA();  
  6.         }else if("PlanB".equals(kind)){  
  7.             return new SimCardPlanB();  
  8.         }else{  
  9.             System.out.println("中国联通尚未推出该套餐");  
  10.             return null;  
  11.         }  
  12.     }  
  13. }  

 移动工厂(ChinaMobileFactory):

Java代码  收藏代码
  1. public class ChinaMobileFactory implements ServiceOperatorFactory {  
  2.     @Override  
  3.     public SimCard getSimCard(String kind) {  
  4.         if("QQT".equals(kind)){  
  5.             return new SimCardQQT();  
  6.         }else if("SZX".equals(kind)){  
  7.             return new SimCardSZX();  
  8.         }else{  
  9.             System.out.println("中国移动尚未推出该套餐");  
  10.             return null;  
  11.         }  
  12.     }  
  13. }  

 联通的A计划、B计划卡和上面一样,不列举了。

 

移动的全球通卡(SimCardQQT):

Java代码  收藏代码
  1. public class SimCardQQT implements SimCard {  
  2.     @Override  
  3.     public void service() {  
  4.         System.out.println("移动全球通电话卡");  
  5.     }  
  6. }  

 移动的神州行卡(SimCardSZX):

Java代码  收藏代码
  1. public class SimCardSZX implements SimCard {  
  2.     @Override  
  3.     public void service() {  
  4.         System.out.println("移动神州行电话卡");  
  5.     }  
  6. }  

 測试类:

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 工厂方法模式(Factory Method Pattern)——定义了一个创建对象的接口, 
  4.      * 但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 
  5.      *  
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         ServiceOperatorFactory factory;  
  10.           
  11.         //获取一个联通A计划的电话卡  
  12.         factory = new ChinaUnicomFactory();  
  13.         SimCard simCard1 = factory.getSimCard("PlanA");  
  14.         simCard1.service();  
  15.           
  16.         //获取一个移动神州行的电话卡  
  17.         factory = new ChinaMobileFactory();  
  18.         SimCard simCard2 = factory.getSimCard("SZX");  
  19.         simCard2.service();  
  20.     }  
  21. }  

 

 

抽象工厂模式(Abstract Factory Pattern)——提供一个接口,用于创建相关或依赖对象的家族,而不须要明白指定详细类。

 

还 是继续上面的样例,如今运营商可不光是卖卡了,还卖手机。你想买个IPhone5,还得分清是买联通版还是移动版。去联通营业厅,肯定不能买到移动版的 IPhone5啊。于是,我们改造一下上面的样例,添加一个电话的接口(Phone),有一个方法打电话(call),定义两个实现类,联通版 IPhone(IPhoneUnicom)和移动版IPhone(IPhoneMobile),在运营商接口中添加一个获取电话的方法 (getPhone,如果营业厅仅仅卖IPhone)。当使用不同的运营商工厂时,得到的电话卡和电话都是指定运营商的,你不须要指定说我要个什么版本号的电 话,由于联通不卖移动版的电话。

 

电话接口(Phone):

Java代码  收藏代码
  1. public interface Phone {  
  2.     public void call();  
  3. }  

 联通版IPhone(IPhoneUnicom):

Java代码  收藏代码
  1. public class IPhoneUnicom implements Phone {  
  2.     @Override  
  3.     public void call() {  
  4.         System.out.println("正在使用IPhone联通版");  
  5.     }  
  6. }  

 移动版IPhone(IPhoneMobile):

Java代码  收藏代码
  1. public class IPhoneMobile implements Phone {  
  2.     @Override  
  3.     public void call() {  
  4.         System.out.println("正在使用IPhone移动版");  
  5.     }  
  6. }  

 运营商接口(ServiceOperatorFactory):

Java代码  收藏代码
  1. public interface ServiceOperatorFactory {  
  2.     public SimCard getSimCard(String kind);  
  3.     public Phone getPhone();  
  4. }  

 联通工厂(ChinaUnicomFactory):

Java代码  收藏代码
  1. public class ChinaUnicomFactory implements ServiceOperatorFactory {  
  2.   
  3.     @Override  
  4.     public SimCard getSimCard(String kind) {  
  5.         if("PlanA".equals(kind)){  
  6.             return new SimCardPlanA();  
  7.         }else if("PlanB".equals(kind)){  
  8.             return new SimCardPlanB();  
  9.         }else{  
  10.             System.out.println("中国联通尚未推出该套餐");  
  11.             return null;  
  12.         }  
  13.     }  
  14.   
  15.     @Override  
  16.     public Phone getPhone() {  
  17.         return new IPhoneUnicom();  
  18.     }  
  19. }  

 移动工厂(ChinaMobileFactory):

Java代码  收藏代码
  1. public class ChinaMobileFactory implements ServiceOperatorFactory {  
  2.   
  3.     @Override  
  4.     public SimCard getSimCard(String kind) {  
  5.         if("QQT".equals(kind)){  
  6.             return new SimCardQQT();  
  7.         }else if("SZX".equals(kind)){  
  8.             return new SimCardSZX();  
  9.         }else{  
  10.             System.out.println("中国移动尚未推出该套餐");  
  11.             return null;  
  12.         }  
  13.     }  
  14.   
  15.     @Override  
  16.     public Phone getPhone() {  
  17.         return new IPhoneMobile();  
  18.     }  
  19. }  

 电话卡接口和实现类同上,不再列举。

測试类:

Java代码  收藏代码
  1. public class Test {  
  2.     /** 
  3.      * 抽象工厂模式(Abstract Factory Pattern)——提供一个接口, 
  4.      * 用于创建相关或依赖对象的家族,而不须要明白指定详细类。 
  5.      *  
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         ServiceOperatorFactory factory;  
  10.           
  11.         //获取一个联通A计划的电话卡  
  12.         factory = new ChinaUnicomFactory();  
  13.         SimCard simCard1 = factory.getSimCard("PlanA");  
  14.         Phone phone1 = factory.getPhone();  
  15.         simCard1.service();  
  16.         phone1.call();  
  17.           
  18.         //获取一个移动神州行的电话卡  
  19.         factory = new ChinaMobileFactory();  
  20.         SimCard simCard2 = factory.getSimCard("SZX");  
  21.         Phone phone2 = factory.getPhone();  
  22.         simCard2.service();  
  23.         phone2.call();  
  24.     }  
  25. }  

 

差别:

 

简单工厂模式多用于需求明白、功能简单的开发,通过一个工厂类就可完毕全部产品的生成工作。

 

工厂方法模式側重点是,开发时不明白要生成哪种类型的实例,不同的工厂类生成不同类型的产品,执行时通过指定不同的工厂类来生成对应的产品类型。如有产品类型A,工厂1用于生成产品A1,工厂2用于生成产品A2,执行时通过指定不同的工厂来生成不同的产品。

 

抽 象工厂模式更注重于生产一个产品家族(即多种类型的产品),而这些产品类型又可横向划分为多个系列,那么,抽象工厂中定义生成全部产品类型的方法,每一个工 厂的实现类用于生产一个系列全部类型的产品。如有A、B、C等产品类型,而这些类型中又可分为多个系列1、2,这样就有了产品A1、A2、B1、B2、 C1、C2,这时,工厂1用于生产A1、B1、C1,工厂2用于生产A2、B2、C2,执行时通过指定不同的工厂来生成不同系列的一组产品。



装饰者模式——动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

    装饰者模式就是给一个对象动态的加入新的功能,装饰者和被装饰者实现同一个接口,装饰者持有被装饰者的实例。JAVA中IO就大量使用了装饰者模式,如:

 

Java代码  收藏代码
  1. InputStream in = new BufferedInputStream(new FileInputStream("test.txt"));  

 其 中FileInputStream、BufferedInputStream都实现了InputStream,BufferedInputStream就 是一个装饰者,添加利用缓冲输入来改进性能,以及FileInputStream所没有的readLine()方法来增强接口。

 

    假如我们有一个系统监控的接口,它的功能非常easy,就是当系统发生异常时进行处理动作。然后我们实现了一个可以日志记录的实现类,当系统发生异常仅仅要把日志 记录好就行了。但是后来我们又想记录完日志后须要给维护人员发邮件,依据开闭原则,我们不能去改动记录日志的类啊,所以这时就须要用到装饰模式了,定义 一个能发送邮件的监控接口实现类,它当中保持了一个监控接口的实例(那个能记日志的实现类),这时,我们调用本实现类时,就即能记录日志,又能发邮件了。 再后来,老大又想加一个系统发生严重异常时能电话通知的功能,没关系,再写一个能电话通知的实现类,什么都不用改,OK了!

 

系统监控的接口:

Java代码  收藏代码
  1. public interface Monitor {  
  2.     //系统发生异常时的处理方法  
  3.     public void handle();  
  4. }  
 记录日志的实现类:
Java代码  收藏代码
  1. public class LoggerMonitor implements Monitor {  
  2.   
  3.     @Override  
  4.     public void handle() {  
  5.         System.out.println("日志已记录;");  
  6.     }  
  7.   
  8. }  
 发送邮件的实现类:
Java代码  收藏代码
  1. public class EmailMonitor implements Monitor {  
  2.       
  3.     private Monitor monitor;  
  4.       
  5.     public EmailMonitor(Monitor monitor){  
  6.         this.monitor = monitor;  
  7.     }  
  8.     @Override  
  9.     public void handle() {  
  10.         monitor.handle();  
  11.         System.out.println("邮件已发送;");  
  12.     }  
  13.   
  14. }  
 电话通知的实现类:
Java代码  收藏代码
  1. public class PhoneMonitor implements Monitor {  
  2.   
  3.     private Monitor monitor;  
  4.       
  5.     public PhoneMonitor(Monitor monitor){  
  6.         this.monitor = monitor;  
  7.     }  
  8.     @Override  
  9.     public void handle() {  
  10.         this.monitor.handle();  
  11.         System.out.println("已电话通知");  
  12.     }  
  13.   
  14. }  

測试类:

Java代码  收藏代码
  1. public class Test {  
  2.   
  3.     /** 
  4.      * 装饰者模式——动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。 
  5.      *  
  6.      * 装饰者模式就是给一个对象动态的加入新的功能,装饰者和被装饰者实现同一个接口,装饰者持有被装饰者的实例。 
  7.      * @param args 
  8.      */  
  9.     public static void main(String[] args) {  
  10.         //既想记录日志又想邮件通知  
  11.         Monitor monitor = new EmailMonitor(new LoggerMonitor());  
  12.         monitor.handle();  
  13.         System.out.println("------------------------------");  
  14.         //既想记录日志又想电话通知  
  15.         monitor = new PhoneMonitor(new LoggerMonitor());  
  16.         monitor.handle();  
  17.         System.out.println("------------------------------");  
  18.         //既想记录日志,又发邮件,又打电话  
  19.         monitor = new PhoneMonitor(new EmailMonitor(new LoggerMonitor()));  
  20.         monitor.handle();  
  21.     }  
  22.   
  23. }  
 

观察者模式——定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的全部依赖者都会收到通知并自己主动更新。

     观察者模式是说,当一个对象的状态发生改变的时候,关心这个对象的全部对象都会接到通知,并作出对应的反应。比方,公司的OA系统提供了消息订阅功能,当有新的消息产生时,全部订阅了该消息的员工都会接到通知,这就是观察者模式。

    观察者模式的核心是一个主题接口,一个观察者接口,不论什么实现了主题接口的实现类都能够被观察者订阅(如会议通知、项目动态都能够被员工订阅),不论什么实现了观察者接口的实现类都能够订阅、或取消订阅一个主题(如中层员工订阅了会议通知、基础员工订阅了项目动态)。

 

主题接口:

Java代码  收藏代码
  1. public interface Subject {  
  2.     //注冊一个观察者  
  3.     public void registerObserver(Observer o);  
  4.     //移除一个观察者  
  5.     public void removeObserver(Observer o);  
  6.     //通知全部观察者  
  7.     public void notifyObservers();  
  8. }  

 观察者接口:

Java代码  收藏代码
  1. public interface Observer {  
  2.     //观察者作出反应  
  3.     public void update();  
  4. }  

 一个消息主题(主题接口的实现类),主题记录了全部的观察者列表和主题的状态,当主题的状态发生改变时,能够通知全部订阅者做出对应的反应:

 

Java代码  收藏代码
  1. import java.util.ArrayList;  
  2.   
  3. public class MsgSubject implements Subject {  
  4.       
  5.     private ArrayList<Observer> observers;//观察者列表  
  6.     private String msg;//消息(状态)  
  7.       
  8.     public MsgSubject(){  
  9.         this.observers = new ArrayList<Observer>();  
  10.     }  
  11.       
  12.     @Override  
  13.     public void registerObserver(Observer o) {  
  14.         observers.add(o);  
  15.     }  
  16.   
  17.     @Override  
  18.     public void removeObserver(Observer o) {  
  19.         int i = observers.indexOf(o);  
  20.         if (i >= 0) {  
  21.             observers.remove(i);  
  22.         }  
  23.     }  
  24.   
  25.     @Override  
  26.     public void notifyObservers() {  
  27.         for (Observer o : this.observers) {  
  28.             o.update();  
  29.         }  
  30.     }  
  31.       
  32.     //获取主题状态的方法  
  33.     public String getMsg() {  
  34.         return msg;  
  35.     }  
  36.       
  37.     //设置主题状态的方法  
  38.     public void setMsg(String msg) {  
  39.         this.msg = msg;  
  40.     }  
  41. }  

 

 观察者1:

Java代码  收藏代码
  1. public class Observer1 implements Observer {  
  2.   
  3.     private MsgSubject msgSubject;//记录主题的引用,当主题状态发生变化时,可向主题获取状态信息  
  4.       
  5.     public Observer1(MsgSubject msgSubject){  
  6.         this.msgSubject = msgSubject;  
  7.         msgSubject.registerObserver(this);//向主题注冊此观察者  
  8.     }  
  9.       
  10.     @Override  
  11.     public void update() {  
  12.         System.out.println("Observer1收到通知:" + this.msgSubject.getMsg());  
  13.     }  
  14.   
  15. }  

 

 观察者2:

Java代码  收藏代码
  1. public class Observer2 implements Observer {  
  2.   
  3.     private MsgSubject msgSubject;//记录主题的引用,当主题状态发生变化时,可向主题获取状态信息  
  4.       
  5.     public Observer2(MsgSubject msgSubject){  
  6.         this.msgSubject = msgSubject;  
  7.         msgSubject.registerObserver(this);//向主题注冊此观察者  
  8.     }  
  9.       
  10.     @Override  
  11.     public void update() {  
  12.         System.out.println("Observer2收到通知:" + this.msgSubject.getMsg());  
  13.     }  
  14.   
  15. }  

 

 測试类:

Java代码  收藏代码
  1. public class Test {  
  2.   
  3.     /** 
  4.      * 观察者模式——定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的全部依赖者都会收到通知并自己主动更新。 
  5.      *  
  6.      * 主题接口的实现类记录了全部的观察者列表和主题的状态,能够向一个主题注冊或取消注冊一个观察者, 
  7.      * 当主题的状态发生改变时,能够通知全部订阅者做出对应的反应 
  8.      * @param args 
  9.      */  
  10.     public static void main(String[] args) {  
  11.         MsgSubject msgSubject = new MsgSubject();  
  12.         Observer1 o1 = new Observer1(msgSubject);  
  13.         Observer2 o2 = new Observer2(msgSubject);  
  14.           
  15.         msgSubject.setMsg("第一条消息");//改变主题状态  
  16.         msgSubject.notifyObservers();//通知全部观察者作出反应  
  17.           
  18.         msgSubject.setMsg("第二条消息");//改变主题状态  
  19.         msgSubject.notifyObservers();//通知全部观察者作出反应  
  20.           
  21.         msgSubject.removeObserver(o1);//观察者1取消订阅  
  22.         msgSubject.setMsg("第三条消息");//改变主题状态  
  23.         msgSubject.notifyObservers();//通知全部观察者作出反应  
  24.     }  
  25.   
  26. }  


Java 下各种设计模式小结