打赏

创建多个“产品”的方式——工厂方法模式总结

简单工厂模式严格来说不算一个设计模式——缺点

继续说之前提到的简单工厂模式,严格来说,这不算一个设计模式……而且在业务量比较多了之后,它也有诸多的隐患

1、由于工厂类集中了所有实例的创建逻辑,这就直接导致一旦这个工厂出了问题,所有的客户端都会受到牵连

2、由于简单工厂模式的产品基于一个共同的抽象类或者接口,这样一来,产品的种类增加的时候,即有不同的产品接口或者抽象类的时候,简单工厂类就需要维护大量的if-else判断,比如导入导出功能,虽然可以用反射或者配置文件,注解等解耦,但是还有更好的办法。

不使用工厂方法模式的例子

还是举那个水果的例子:之前水果园子里只有苹果和香蕉两种水果,现在又新加一个梨的品种。简单工厂类的代码如下:

// 简单工厂类
public class FruitFactoryA {
    public static FruitA getFruit(String type) {
        if ("apple".equalsIgnoreCase(type)) {
            return new AppleA();
        } else if ("banana".equalsIgnoreCase(type)) {
            return new BananaA();
        } else if ("pear".equalsIgnoreCase(type)) {
            return new PearA();
        } else {
            System.out.print("error!");
        }

        return null;
    }
}

该例子使用简单工厂模式,工厂类过于臃肿。

public class Main {
    public static void main(String[] args) {
        FruitA apple = FruitFactoryA.getFruit("apple");
        FruitA banana = FruitFactoryA.getFruit("banana");
        FruitA pear = FruitFactoryA.getFruit("pear");

        // 不太好,没有检测null异常,演示
        apple.get();
        banana.get();
        pear.get();
    }
}

因为简单工厂模式只有一个工厂类,它需要处理所有的对象创建的逻辑。如果日后需求不断增加,则后果不堪设想,违背了单一职责,导致系统丧失灵活性和可维护性。而且更重要的是,简单工厂模式违背了OCP——开放封闭原则。当然还有单一职责原则(虽然这里没具体体现)。那么如何改进呢?

引入工厂方法模式——让子类去创建对象

如何才能实现工厂类的代码不去修改,而开放扩展——可以把工厂的职责抽象,抽象if-else判断逻辑,把每一个判断都抽象为一个工厂类,而这些具体的工厂类统一抽象为一个接口来约束具体子类去生产产品,这就是传说中的——工厂方法模式,它同样属于类的创建型模式,又被称为多态工厂模式。

工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口(仅仅起到一个约束生产动作的作用),这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

工厂方法模式实现

抽象工厂角色:工厂方法模式的核心,任何工厂类都必须实现这个接口

public interface FactoryA {
    FruitA getFruit();
}

具体工厂( Concrete  Creator)角色;具体工厂类是抽象工厂的一个实现,负责实例化产品对象

public class AppleFactoryA implements FactoryA {
    @Override
    public FruitA getFruit() {
        return new AppleA();
    }
}

public class BananaFactoryA implements FactoryA {
    @Override
    public FruitA getFruit() {
        return new BananaA();
    }
}

public class PearFactory implements FactoryA {
    @Override
    public FruitA getFruit() {
        return new PearA();
    }
}

抽象产品(Product)角色;工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口

public interface FruitA {
    void get();
}

具体产品(Concrete Product)角色;工厂方法模式所创建的具体实例对象

public class AppleA implements FruitA {
    @Override
    public void get() {
        System.out.println("苹果");
    }
}

public class BananaA implements FruitA {
    @Override
    public void get() {
        System.out.println("香蕉");
    }
}

public class PearA implements FruitA {
    @Override
    public void get() {
        System.out.println("梨");
    }
}

客户端调用

public class Main {
    public static void main(String[] args) {
        // 得到对应的水果的工厂
        FactoryA appleF = new AppleFactoryA();
        FactoryA bananaF = new BananaFactoryA();
        FactoryA peatF = new PearFactory();

        // 通过各个工厂去得到对应的水果
        FruitA apple = appleF.getFruit();
        FruitA banana = bananaF.getFruit();
        FruitA pear = peatF.getFruit();

        apple.get();
        banana.get();
        pear.get();
    }
}

如上代码,如果以后有新的水果出现,比如橘子,那么不用修改工厂类,只需要增加一个橘子以及橘子的工厂类,且橘子工厂同时去遵循工厂的接口即可,客户端调用就可以了,而其他已经写好的工厂类无需修改!完全符合OCP原则,同时每个具体工厂子类只负责对应水果的生成,也遵守了单一职责原则

类图如下

简单工厂和工厂方法模式对比

工厂方法模式与简单工厂模式在结构上的不同不是很明显。

1、工厂方法类的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上。

2、工厂方法模式之所以有一个别名叫多态性工厂模式,是因为具体工厂类都有共同的接口,或者有共同的抽象父类。

系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要任何修改,也不需要修改客户端原有的代码,很好的符合了“开放-封闭”原则。

而简单工厂在添加新产品对象后,不得不修改工厂方法,扩展性不好。

工厂方法模式退化后可以演变成简单工厂模式。

JDK 中的工厂方法模式

JAVA的 API 里使用了工厂方法模式的也很多很多,常用的比如,Object 类里的 toString() 方法,Java 的任何类,都可以继承或者覆写toString 方法,Object 相当于抽象工厂类,各个Java 的类,相当于具体工厂类。

还有 util 包里的Calendar类,该类是一个抽象类,它为特定瞬间与一组诸如 YEARMONTHDAY_OF_MONTHHOUR 等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
    public static Calendar getInstance() {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }
}

该节选的代码片段有一个静态的 getInstance() 方法,return 了一个具体的日历对象,Calendar 是工厂方法模式里的抽象工厂类,而具体工厂子类(简单举几个例子)有JapaneseImperialCalendar,GregorianCalendar……

createCalendar 方法是 private 的,是属于 getInstance 方法里的,也就是说 Calendar 这个抽象工厂类返回具体子类的对象,JDK 里这样说:

与其他语言环境敏感类一样,Calendar 提供了一个类方法 getInstance,以获得此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化……

客户端通过该方法返回对应的日历工厂,客户端再通过这些日历工厂去生成对应的日历产品……将来如果需要支持某个其他地区的特殊历法,程序员除了必要的增加对应的日历工厂并 extends Calendar 这个抽象工厂,且增加对应的日历产品之外,只需要增加 Calendar 的 getInstance 方法的新的逻辑,但 Calendar 的使用者无需承担这种变化的影响,符合OCP。

欢迎关注

dashuai的博客是终身学习践行者,大厂程序员,且专注于工作经验、学习笔记的分享和日常吐槽,包括但不限于互联网行业,附带分享一些PDF电子书,资料,帮忙内推,欢迎拍砖!

 

posted @ 2019-02-05 22:42  dashuai的博客  阅读(1379)  评论(0编辑  收藏  举报
Flag Counter欢迎关注微信公众号