工厂方法模式(3.3)

3.3.1工厂方法模式(3.3)     返回目录


工厂方法模式(factory method pattern)从2方面认识。

  • 结构推导。参数化工厂使用分支语句违背OCP,如果以多态来重构分支结构,可以从静态工厂模式演化出工厂方法模式的结构。
  • 使用意图。工厂方法模式并非作为静态工厂模式的改良版本。换言之,它并不是为了支持Demo→IMan,而是为了解决类型匹配问题

1.以多态来重构分支结构

以多态来重构if-else、switch-case等分支结构是重构的基本技术,在参数化工厂的代码(见例程3-10)基础上,通过重构可以获得工厂方法模式的结构。,如例程3-14所示。

重构的目标是将分支结构的每一个分支,放在一个子类的@Override函数中,而父类型的抽象方法,由包含分支结构的函数如create承担,create的参数如typeName用于分支判断,则删除该参数。

重构过程如下:

①从包含分支结构的函数create获得抽象方法,并封装到抽象类型IManFactory中。注意将静态方法改为实例方法。

②IManFactory的各子类型创建一一对应的IMan的子类型。
package chap3.factoryP;
import chap3.init.IMan; 
public interface IManFactory{
    public  IMan create();
}
package chap3.factoryP;
import chap3.init.*;//IMan类层次
public class BoyFactory implements IManFactory{
    @Override public Boy create(){
        return new Boy();
    }
}//IMan的每一个子类型有一个IManFactory具体工厂对应,略
// chap3.factoryP.Demo
    public static void test(){
        IMan man = new BoyFactory().create();//new Boy()
        man.foo();
    }


它体现了一种非常重要的思考方式——以多态来重构if-else或switch-case结构。从重构分支结构的角度看,策略模式与[2.1.3工厂方法模式(3.3)]和[4.2状态模式(5.8)]是三胞胎。

重构虽然获得了工厂方法模式的结构,但是 会诱导工厂方法模式的意图。如果以Client依赖抽象类型IServer为目的而使用 工厂方法模式,会显得十分别扭,这是典型的为了模式而模式的应用。因为Client依赖抽象类型IServer的代价,是Client现在依赖依赖具体的工厂类。
 

2.解决类型匹配

工厂方法模式的目的,它不同于静态工厂模式、God、Spring,后者都是依赖抽象类型的使能工具,工厂方法模式不是!

很多程序员在认识到静态工厂模式的缺点后,会认为工厂方法模式是静态工厂模式对升级版。GoF对工厂方法模式的描述为:『定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类』。这种解释给人的印象是,若Demo→IMan,工厂方法模式在遇到添加IMan新子类时,不再需要不修改静态工厂的代码而是添加新的工厂子类,符合OCP。

GoF在工厂方法模式的适应性中指出:“当一个类不知道它所需要的对象的类时”,即Demo不知道它需要IMan的具体子类。这也使得很多程序员认为工厂方法模式是抽象依赖原则的使能工具。但是,Demo从不依赖IMan的具体子类变成依赖IManFactory的具体子类,仍然违反了抽象依赖原则。绝大多数情况下,这种使用方式是多此一举,是典型的为了模式而模式的做法

程序员可以使用通用的工具God和Spring,或者静态工厂模式这种不完美但简洁明快的工具,满足Demo→IMan。

使用工厂方法模式的合理情景/最佳实践,当Demo同时依赖有强烈的对应关系的两个类层次IA和IB时,它解决类型匹配问题。

例如Demo依赖交通工具/IVehicle和修理店/IRepairShop。IVehicle有多个子类型如轿车、单车和牛车,如果IVehicle对象出现问题,则需要进行修理。显而易见,Demo不应该分别地创建IVehicle-IRepairShop对象,否则理论上不能够保证它们匹配,如将轿车拖到了单车修理店。于是,在IVehicle中设计一个抽象方法也即工厂方法,以获得对应的IRepairShop 对象。代码如例程3-12所示。

package chap3.factoryP; //其他类包语句略
public interface IRepairShop{//产品类
    void repair();
}//有Car4s、路边轿车修理店Low、单车修理店等,略

public interface IVehicle{ //工厂类
    public void move();
    public IRepairShop getRepairShop(int money); //工厂方法
} 

import static yqj2065.util.Print.pln;
public class Car implements IVehicle{
    public void move(){
        pln("Car move");
    }
    public IRepairShop getRepairShop(int money){
        if(money>3000){
            return new Car4s();
        }else{
            return new Low();
        }
    }
}
//Demo
    public static void main(String[] args) {
        IVehicle car =new Car();
        car.move();
        IRepairShop repair = car.getRepairShop(2500); //
        repair.repair();
        car.move();
    }

工厂方法模式的最佳实践,是Demo关注和依赖工厂类层次IVehicle,而对于工厂的产品即IRepairShop对象,Demo甚至可以一无所知。代码中创建具体的工厂对象如new Car()的这种用法,暗示工厂方法模式不是抽象依赖原则的使能工具。

★工厂方法模式(factory method pattern),解决类型匹配问题。Client依赖有强烈的对应关系的两个类层次IA和IB,则IA定义一个工厂方法以获得与IA对象匹配的IB对象。

例如,在创建链表时,应用程序员通常不介意使用new表达式,如List list = new ArrayList()。而JDK的设计者,不可能希望/要求程序员创建一个数据结构的同时,再去创建对应的数据结构的迭代器对象。因此,各种数据结构如List都是接口Iterable(可迭代的)的子类型,Iterable封装一个抽象方法,该方法是一个工厂方法——返回一个迭代器Iterator。

public interface Iterable{

    Iterator iterator();//返回一个迭代器

}


编程实践中,通常工厂是Client关注的主体,而工厂生产的产品被Client所忽视。例如Client开车ICar,不管什么车Client都可以开。现在,Car要找它的4S店/I4S,Client会关注各种4S店吗?

 
由于受通用工具类如God——反射+配置的压制,现在应用程序中使用工厂方法模式比较少见。

最后更新2021.2.18 修改例子。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值