不要为了用设计模式而用设计模式

近来准备从通讯设备行业中面向业务,面向流程的开发模式转型,去拥抱OOD/OOP,拥抱互联网。要学习面向对象设计,必然要面对的软件灵活性,复用性的问题。而解决这一问题的快速方式是采用合适的设计模式去增强软件的灵活性和复用性。

Christopher Alexander说过“每一个设计模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动

设计模式,给我们带来以下便利:

设计模式使人们可以更加简单方便地复用成功的设计和体系结构。

将已证明的技术表述成设计模式也会使新系统开发者更容易理解其设计思路。

设计模式帮助你做出有利于系统复用的选择,避免设计损害系统的复用性。

通过提供一个显式类和对象作用关系以及他们之间的潜在联系的说明规范,设计模式可以提高已有系统的文档管理和系统维护的有效性

但在学习设计模式的过程中的一个明显误区是,我们记住了设计模式的形态,却不了解设计模式在什么地方解决问题。就好比学了降龙十八掌的招式,却不知道在什么情况下该出什么招。

以下,是我在学习过程中看到的一个例子:

----------------------------------------------------------例子----------------------------------------------------------

【3】如何用java语言来实现该模式
背景:用一个分别对不同数据库(Oracle 或 SQL Server)中表( User 和 Department )的操作的实例来展示该设计模式。先看下代码的结构图:

不要为了用设计模式而用设计模式_第1张图片

3.1 首先定义两个抽象的产品类:IUser.java 和 IDepartment.java。

IUser.java的源码:

[html]  view plain copy
  1. package com.andyidea.patterns.abstractproduct;  
  2.   
  3. /**  
  4.  * 抽象产品角色:User接口  
  5.  * @author Andy.Chen  
  6.  *  
  7.  */  
  8. public interface IUser {  
  9.   
  10. }  
IDepartment.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.abstractproduct;  
  2.   
  3. /**  
  4.  * 抽象产品角色:Department接口  
  5.  * @author Andy.Chen  
  6.  *  
  7.  */  
  8. public interface IDepartment {  
  9.   
  10. }  
3.2 定义抽象工厂类:IDBFactory.java

[html]  view plain copy
  1. package com.andyidea.patterns.abstractfactory;  
  2.   
  3. import com.andyidea.patterns.abstractproduct.IDepartment;  
  4. import com.andyidea.patterns.abstractproduct.IUser;  
  5.   
  6. /**  
  7.  * 抽象工厂角色:工厂接口  
  8.  * @author Andy.Chen  
  9.  *  
  10.  */  
  11. public interface IDBFactory {  
  12.   
  13.     public IUser createUser();  
  14.     public IDepartment createDepartment();  
  15. }  
3.3创建具体产品角色类:OracleOfUser.java;OracleOfDepartment.java;SQLServerOfUser.java;SQLServerOfDepartment.java。分别继承IUser.java和IDepartment.java。

OracleOfUser.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.concreteproduct;  
  2.   
  3. import com.andyidea.patterns.abstractproduct.IUser;  
  4.   
  5. /**  
  6.  * 具体产品角色:Oracle中的User  
  7.  * @author Andy.Chen  
  8.  *  
  9.  */  
  10. public class OracleOfUser implements IUser{  
  11.       
  12.     public OracleOfUser(){  
  13.         System.out.println("Oracle工厂:在Oracle中操作User表.");  
  14.     }  
  15.   
  16. }  
OracleOfDepartment.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.concreteproduct;  
  2.   
  3. import com.andyidea.patterns.abstractproduct.IDepartment;  
  4.   
  5. /**  
  6.  * 具体产品角色:Oracle中的Department  
  7.  * @author Andy.Chen  
  8.  *  
  9.  */  
  10. public class OracleOfDepartment implements IDepartment{  
  11.       
  12.     public OracleOfDepartment(){  
  13.         System.out.println("Oracle工厂:在Oracle中操作Department表.");  
  14.     }  
  15.   
  16. }  
SQLServerOfUser.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.concreteproduct;  
  2.   
  3. import com.andyidea.patterns.abstractproduct.IUser;  
  4.   
  5. /**  
  6.  * 具体产品角色:SQL Server中的User  
  7.  * @author Andy.Chen  
  8.  *  
  9.  */  
  10. public class SQLServerOfUser implements IUser{  
  11.       
  12.     public SQLServerOfUser(){  
  13.         System.out.println("SQL Server工厂:在SQL Server中操作User表.");  
  14.     }  
  15.   
  16. }  
SQLServerOfDepartment.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.concreteproduct;  
  2.   
  3. import com.andyidea.patterns.abstractproduct.IDepartment;  
  4.   
  5. /**  
  6.  * 具体产品角色:SQL Server中的Department  
  7.  * @author Andy.Chen  
  8.  *  
  9.  */  
  10. public class SQLServerOfDepartment implements IDepartment{  
  11.       
  12.     public SQLServerOfDepartment(){  
  13.         System.out.println("SQL Server工厂:在SQL Server中操作Department表.");  
  14.     }  
  15.   
  16. }  
3.4 创建具体工厂类:OracleFactory.java和SQLServerFactory.java。

OracleFactory.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.concretefactory;  
  2.   
  3. import com.andyidea.patterns.abstractfactory.IDBFactory;  
  4. import com.andyidea.patterns.abstractproduct.IDepartment;  
  5. import com.andyidea.patterns.abstractproduct.IUser;  
  6. import com.andyidea.patterns.concreteproduct.OracleOfDepartment;  
  7. import com.andyidea.patterns.concreteproduct.OracleOfUser;  
  8.   
  9. /**  
  10.  * 具体工厂角色:Oracle工厂  
  11.  * @author Andy.Chen  
  12.  *  
  13.  */  
  14. public class OracleFactory implements IDBFactory{  
  15.   
  16.     @Override  
  17.     public IUser createUser() {  
  18.         return new OracleOfUser();  
  19.     }  
  20.   
  21.     @Override  
  22.     public IDepartment createDepartment() {  
  23.         return new OracleOfDepartment();  
  24.     }  
  25.   
  26. }  
SQLServerFactory.java源码:

[html]  view plain copy
  1. package com.andyidea.patterns.concretefactory;  
  2.   
  3. import com.andyidea.patterns.abstractfactory.IDBFactory;  
  4. import com.andyidea.patterns.abstractproduct.IDepartment;  
  5. import com.andyidea.patterns.abstractproduct.IUser;  
  6. import com.andyidea.patterns.concreteproduct.SQLServerOfDepartment;  
  7. import com.andyidea.patterns.concreteproduct.SQLServerOfUser;  
  8.   
  9. /**  
  10.  * 具体工厂角色:SQL Server工厂  
  11.  * @author Andy.Chen  
  12.  *  
  13.  */  
  14. public class SQLServerFactory implements IDBFactory{  
  15.   
  16.     @Override  
  17.     public IUser createUser() {  
  18.         return new SQLServerOfUser();  
  19.     }  
  20.   
  21.     @Override  
  22.     public IDepartment createDepartment() {  
  23.         return new SQLServerOfDepartment();  
  24.     }  
  25.   
  26. }  
3.5 客户端测试类:AbstractFactoryClient.java

[html]  view plain copy
  1. package com.andyidea.patterns.client;  
  2.   
  3. import com.andyidea.patterns.abstractproduct.IDepartment;  
  4. import com.andyidea.patterns.abstractproduct.IUser;  
  5. import com.andyidea.patterns.concretefactory.OracleFactory;  
  6. import com.andyidea.patterns.concretefactory.SQLServerFactory;  
  7.   
  8. /**  
  9.  * 抽象工厂测试类  
  10.  * @author Andy.Chen  
  11.  *  
  12.  */  
  13. public class AbstractFactoryClient {  
  14.   
  15.     public static void main(String[] args) {  
  16.           
  17.         System.out.println("Welcome to Andy.Chen Blog!" +"\n"   
  18.                    +"Abstract Factory Patterns." +"\n"  
  19.                    +"-------------------------------");  
  20.           
  21.         IUser oracleUser,sqlUser;  
  22.         IDepartment oracleDept,sqlDept;  
  23.         OracleFactory of = new OracleFactory();  
  24.         SQLServerFactory sf = new SQLServerFactory();  
  25.           
  26.         oracleUser = of.createUser();  
  27.         oracleDept = of.createDepartment();  
  28.         sqlUser = sf.createUser();  
  29.         sqlDept = sf.createDepartment();  
  30.     }  
  31. }  
【4】程序运行结果:

  • public class AbstractFactoryClient {  
  •   
  •     public static void main(String[] args) {  

  •         IUser oracleUser,sqlUser;  
  •         IDepartment oracleDept,sqlDept;  
  •         OracleFactory of = new OracleFactory();  
  •         SQLServerFactory sf = new SQLServerFactory();  
  •           
  •         oracleUser = of.createUser();  
  •         oracleDept = of.createDepartment();  
  •         sqlUser = sf.createUser();  
  •         sqlDept = sf.createDepartment();  
  •     }  

  • [html]  view plain copy
    1. Welcome to Andy.Chen Blog!  
    2. Abstract Factory Patterns.  
    3. -------------------------------  
    4. Oracle工厂:在Oracle中操作User表.  
    5. Oracle工厂:在Oracle中操作Department表.  
    6. SQL Server工厂:在SQL Server中操作User表.  
    7. SQL Server工厂:在SQL Server中操作Department表.  
    ----------------------------------------------------------例子结束----------------------------------------------------------

    可以看到,在例子中,作者正确的按照了AbstractFactory的方式定义了AbstractFactory类,ConcreteFactory类,AbstractProduct类和ConcreteProduct类。但却没有完全明白AbstractFactory模式解决的是哪类复用问题,在什么情况下被使用。

    AbstractFactory接口确定了可以被创建的产品的集合。使用AbstractFactory,使得我们易于交换产品系列有利于产品的一致性

    在这段client代码中,系统的作用被定义为创建所有产品,这里并不存在说我们要复用这段代码,因为所有定义在AbstractFactory产品已经被创建出来,它已经使“复用代码,使设计易于交换产品系列”这个命题失效。因此,在这种情况下套用AbstractFactory模式,并没有得到我们想要的效果。 

    正确的使用场景应该是AbstractFactoryClient用于创建用户数据,并且该用户数据可能存储在不同的数据库系统中,为了使AbstractFactoryClient可以针对不同的数据库系统复用同一段代码,我们才考虑去采用AbstractFactory模式


    当client代码写成如下模式的时候我们才考虑用AbstractFactory模式去优化它


  • public class AbstractFactoryClient {  
  •   
  •     public static void main(String[] args) {  
  •           
  • SQLOfUser user = new SQLOfUser();
  • SQLServerOfDepartment Dept = new SQLServerOfDepartment();
  •     }  

  • AbstractFactoryClient用createData去创建它需要的数据,并且创建的过程是调用了虚(abstract)函数(interface),使得这段代码可以被复用,随时根据所需去改变实例化的类,替换Product。


    1. public class AbstractFactoryClient {  
    2.   
    3.     public static void main(String[] args) {  
    4.           
    5. IUser user;
    6. IDepartment dept;
    7. //用户根据产品类型,复用一下代码
    8. IDBFactoryfactory = new SQLFactory(); //or new oracleFactory()
    9. CreateData(factory);
    10.     }
    11.     public CreateData(IDBFactory factory){
    12.         user = Factory.createUser();
    13.         Dept = Factroy.createDepartment(); 
          )  
    14. }  

    总结:在使用设计模式之前,一定要搞清楚我们的问题是什么,选用的设计模式使解决什么样的特定设计问题。作者最原始的代码没有问题,但如果我们没有client端去看为什么要使用这种设计模式时,我们就很容易走入为使用设计模式而使用设计模式的误区。要记住,设计模式永远是为了让你的软件更具灵活性和复用性,而不是让你代码看起来很漂亮,用了很多设计模式!


  • 你可能感兴趣的:(不要为了用设计模式而用设计模式)