职业IT人-IT人生活圈

 找回密码
 成为会员
搜索
查看: 377|回复: 9

工厂方法模式应该是这样的

[复制链接]
有烟没火 发表于 2011-9-2 11:00 | 显示全部楼层 |阅读模式
    在网上看到工厂方法的模式讲解,作者们都有意无意的跟简单工厂模式联系在一起,我认为这本身似乎就是个错误。我觉得这个模式更像是外观模式的一个变形。
首先来讲简单工厂模式。如一个接口A, 实现类有A1,A2,A3.
在客户端上调用 A a=new A1(); 很多人觉得这样不好,因为这样就把接口跟具体的实现给耦合了。好吧,我也觉得不好,但是我先不会换成简单工厂,而是会这样:
  
A a=null;   
if(type==1){   
a=new A1();   
}else if(type==2){   
a=new A2();   
}else if(type==3){   
a=new A3();   
} ;  

A a=null;
if(type==1){
a=new A1();
}else if(type==2){
a=new A2();
}else if(type==3){
a=new A3();
} ;

但是上面的代码还是不好,因为可能调用这个if else判断的客户端不只有这里,其他的地方也要进行这样的判断,而且判断的逻辑是一样的。于是要考虑封装,自然而然的就要用简单工厂了。 其本质应该是
对A接口的实现类的选择的逻辑的封装
但是工厂方法模式:定义一个抽象的工厂方法,在子类中实现具体应该生成哪个A的接口。看上去确实生成A接口的选择被延时到子类。但是在客户端上调用还是有个很大的问题:谁来选择具体工厂方法模式中的工厂? 简单工厂模式是把这个逻辑封装了的,到了工厂方法模式这个选择是由客户端自己来选择的:
  
AmethodFactory am=new A1methodFactory();//对应生成对象A1的工厂  

AmethodFactory am=new A1methodFactory();//对应生成对象A1的工厂

下面我们来看看工厂方法模式是如何手jian的。

其实
  
A a=new A1();  

A a=new A1();
这段代码已经很面向接口编程了。因为后面的操作都是用接口A的引用a进行具体的操作的。
我就是不知道选择new 哪个A的具体实现才用工厂方法模式的。结果工厂方法模式让我这么办
  
AmethodFactory am=new A1methodFactory();   
//我怎么知道选A1methodFactory 而不是选A2methodFactory的?   
A a=am.createA();//当然标准的做法这个方法是不要对外暴露的  

AmethodFactory am=new A1methodFactory();
//我怎么知道选A1methodFactory 而不是选A2methodFactory的?
A a=am.createA();//当然标准的做法这个方法是不要对外暴露的

这样的实现根本就没有起到把我将选择的逻辑封装起来,我还要多加个工厂AmethodFactory  还得在上面加实现
用来“开闭”我需求的增加。  我认为为了开闭的话,下面的代码就够了
  
A a=new A1();  

A a=new A1();反正客户端已经知道怎么选了,换个实现就行了嘛
A a=new A2();
A a=new A3();
A a=new A3();
要加需求就在加个类实现A接口,在这里在new之就行了,非常的开闭了。似乎是没有必要弄什么工厂方法模式的。

但是工厂模式存在也是有他的原因的,不过不能按简单工厂的思路想,不妨按外观模式的思路想想。
客户端需要一个A的实现完成一定的功能,如
  
A a=new A1();   
a.test(1);   
a.test2("soft");  

A a=new A1();
a.test(1);
a.test2("soft");但是客户端其实不是需要a, 他需要的是个整体,是
  
a.test(1);   
a.test2("soft");  

a.test(1);
a.test2("soft");

于是需要外观模式封装之。。。
  
class Facade{   
public void face(int i,String soft){   
a.test(i);   
a.test2(soft);   
}   
  
}  

class Facade{
public void face(int i,String soft){
a.test(i);
a.test2(soft);
}

}

face方法里面的逻辑一般上是固定的。 但是问题是
face方法依赖的a是个接口,不是实现类。自然而然的需要加入所谓的工厂方法,
于是有:

  
class Facade{   
  
public abstract create();   
public void face(int i,String soft){//关键是这个方法的逻辑比较固定,这时候用继承的方式更好   
//继承的目的其实主要是这个方法在子类里面不用写了   
A a=create();   
a.test(i);   
a.test2(soft);   
}   
  
}  

class Facade{

public abstract create();
public void face(int i,String soft){//关键是这个方法的逻辑比较固定,这时候用继承的方式更好
//继承的目的其实主要是这个方法在子类里面不用写了
A a=create();
a.test(i);
a.test2(soft);
}

}


这个Facade就变成了一个工厂方法了。 所以很多设计模式在讲到
工厂方法模式的时候会说 里面的create方法最好不给外面调用的。因为其实客户端不是需要对象
A,是需要完成特定的功能,这里是face方法(他才是客户端真正需要的),只是这些功能实现又依赖A罢了。
工厂模式跟简单工厂设计理念是不同的,他没有将选择的逻辑封装,而更像是个外观模式的变体。
其实像下面的这种需求,根本就不需要使用工厂方法模式的:
  
A a=new A1();   
a.test();// 客户端就只调这么一个方法,没有其他更复杂的逻辑了   
//如果手不jian实在不需要在搞个工厂方法模式出来。  

A a=new A1();
a.test();// 客户端就只调这么一个方法,没有其他更复杂的逻辑了
//如果手不jian实在不需要在搞个工厂方法模式出来。



大家觉得如何?

话说我当年 发表于 2011-9-2 11:00 | 显示全部楼层
我表示看不懂

hxy 发表于 2011-9-2 11:00 | 显示全部楼层
  
//Context是Android里的,该方法用来创建view   
public static <T extends BaseUnit> T createUnit(Class<T> cls,Context context) {   
        BaseUnit bu = null;   
        try {   
            bu = (BaseUnit)Class.forName(cls.getName()).getConstructor(Context.class).newInstance(context);   
        } catch (IllegalAccessException e) {   
            e.printStackTrace();   
        } catch (InstantiationException e) {   
            e.printStackTrace();   
        } catch (ClassNotFoundException e) {   
            e.printStackTrace();   
        } catch (IllegalArgumentException e) {   
            e.printStackTrace();   
        } catch (SecurityException e) {   
            e.printStackTrace();   
        } catch (InvocationTargetException e) {   
            e.printStackTrace();   
        } catch (NoSuchMethodException e) {   
            e.printStackTrace();   
        }   
        return (T)bu;   
    }  

//Context是Android里的,该方法用来创建view
public static <T extends BaseUnit> T createUnit(Class<T> cls,Context context) {
                BaseUnit bu = null;
                try {
                        bu = (BaseUnit)Class.forName(cls.getName()).getConstructor(Context.class).newInstance(context);
                } catch (IllegalAccessException e) {
                        e.printStackTrace();
                } catch (InstantiationException e) {
                        e.printStackTrace();
                } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                } catch (SecurityException e) {
                        e.printStackTrace();
                } catch (InvocationTargetException e) {
                        e.printStackTrace();
                } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                }
                return (T)bu;
        }

我很懒,一般都这么用

江南枫 发表于 2011-9-2 11:01 | 显示全部楼层
请教一下
public class buildhouse(){  //根据不同的参数类型
...
public gethouse(args){
switch(args.type){
case "red":new redhouse(args).getHouse(); break;
                        case "blue":new bluehouse(args).getHouse();break;
}

}
...
}

public interface house(){
void setcolor(args);
        void setwin(args);
void setdoor(args);
        String getHouse();
}

public class redhouse implements house{
void setcolor(args){color = args.color;}
void setwin(args){ winnumber = args.winnum; }
        void setdoor(args) { door = 1;
                             door.setColor("black");
                             door.setclock(1);
}
        String getHouse(args){
this.setcolor(args);
this.setwin(args);
this.setdoor(args);
};
}


public class bluehouse implements house{
void setcolor(args){color = args.color;}
void setwin(args){ winnumber = args.winnum; }
        void setdoor(args) { door = 2;
                             door.setColor("yellow");
                             door.setclock(1);
}
        String getHouse(args){
this.setcolor(args);
this.setwin(args);
this.setdoor(args);
};
}


如果需要增加新类型的房子,需要做的事情:
1,增加一个新的类
2,修buildhouse()的调用

如果buildhouse()需要增加新的方法,需要做的事情:
1,在buildhouse()中增加方法
2,修改所有的子类

问题:
class redhouse与class bluehouse中的方法,setcolor,setwin内容都是一样的,这样算代码重复吗?

第二种方式

public class buildhouse(){
...
public gethouse(args){
color = args.color;
winnumber = args.winnum;
        if(args.type=="red") {
door = 1;
                        door.setColor("black");
}
if(args.type=="blue"){
     door = 2;
                             door.setColor("yellow");
                           
}
door.setclock(1);

}
...
}

如果需要增加新类型的房子,需要做的事情:
需要修改gethouse的方法gethouse

问题:
每次需要修改代码的时候,都要修改gethouse.如果某些参数类型是不同的处理,又需要重新修改buildhouse.
如:

public class buildhouse(){
...
public gethouse(args){
color = args.color;
winnumber = args.winnum;
        if(args.type=="red") {
door = 1;
                        door.setColor("black");
}
if(args.type=="blue"){
     door = 2;
                             door.setColor("yellow");
                           
}
if(args.type=="green"){   //新增加的类型的处理与原来的不同
door.setclock(3);
}else{
door.setclock(1);
}
}
...
}


两种方式相比,哪种比较好?原理是什么

Jethro 发表于 2011-9-2 11:01 | 显示全部楼层
我觉得楼主钻研的太细了。
工厂方法,外观模式从目的上来说,是完全不同的,没有必要放在一起讨论。

设计模式中,功能相近或者结构相似的模式有很多,不用像答题一样,花费大量时间,把各个细微之处都抠得这么仔细。

天上智喜 发表于 2011-9-2 11:01 | 显示全部楼层
看不懂……

楠楠 发表于 2011-9-2 11:01 | 显示全部楼层
工厂方法模式最简单的说法就是:用一维的工厂族对应二维的产品族

只学java 发表于 2011-9-2 11:01 | 显示全部楼层
我也表示看不懂

月上萧萧 发表于 2011-9-2 11:01 | 显示全部楼层
我硬是没看懂!

找不到我 发表于 2011-9-2 11:02 | 显示全部楼层
最应该关心的应该是某个模式是用于在什么环境下解决什么问题、怎么解决问题,以及为什么要这样解决问题的。很多文章太表面化、形式化,看了都懂,就是跟实际应用联系不上,过后全忘。
您需要登录后才可以回帖 登录 | 成为会员

本版积分规则

QQ|手机版|小黑屋|网站帮助|职业IT人-IT人生活圈 ( 粤ICP备12053935号-1 )|网站地图
本站文章版权归原发布者及原出处所有。内容为作者个人观点,并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是信息平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽造成漏登,请及时联系我们,我们将根据著作权人的要求立即更正或者删除有关内容。

GMT+8, 2024-5-5 22:01 , Processed in 0.145041 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表