美文网首页设计模式笔记
设计模式笔记(四): 工厂模式

设计模式笔记(四): 工厂模式

作者: yeonon | 来源:发表于2018-07-23 11:50 被阅读13次

    new是创建对象实例时最常用的方法(不是唯一方法,反射也可以创建实例),但是一旦涉及到new,就会涉及到具体实现类,而非接口,这就可能带来一些问题。例如下述代码:

    Car car = new Car();
    Wheel wheel = null;
    if (type.equal("A")) {
      wheel = new AWheel();
    }  else if (type.equal("B")) {
      wheel = new BWheel();
    } else if (type.equal("C")) {
      wheel = new CWheel();
    }
    car.setWheel(wheel);
    

    很简单的代码,创建一个汽车对象和轮子对象,然后根据type来选择使用哪种轮子。这里代码是有问题的,如果随着时间的变化,A轮子不生产了,也就是A轮子不能使用了,那我们就不得不修改代码,将A相关的代码移除,但有很多其他客户端也是使用这样的代码,这意味着其他客户端的代码也必须得改。换句话说,这里一系列的判断是会“变化”的部分。比较好的方法是将其封装起来,单独放到一个地方去,以后如果有变化,只需改动一处即可。这就是简单工厂模式。

    简单工厂模式

    我们换个例子,现在来看看一个披萨的例子。如下代码所示:

    //Pizza抽象类
    public abstract class Pizza {
    
        protected String name;
    
        public abstract void bake();
        public abstract void cut();
    
        @Override
        public String toString() {
            return "Pizza{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    //A类型Pizza
    public class APizza extends Pizza {
    
        public APizza() {
            this.name = "A Pizza";
        }
    
        @Override
        public void bake() {
            System.out.println("A bake");
        }
    
        @Override
        public void cut() {
            System.out.println("A cut");
        }
    }
    
    //B类型Pizza
    public class BPizza extends Pizza {
    
        public BPizza() {
            this.name = "B Pizza";
        }
    
        @Override
        public void bake() {
            System.out.println("B bake");
        }
    
        @Override
        public void cut() {
            System.out.println("B cut");
        }
    }
    
    //Pizza商店
    public class PizzaStore {
    
        public Pizza orderPizza(String type) {
            Pizza pizza = null;
            if ("A".equals(type)) {
                pizza = new APizza();
            } else if ("B".equals(type)) {
                pizza = new BPizza();
            }
            if (pizza != null) {
                pizza.bake();
                pizza.cut();
            }
            return pizza;
        }
    }
    
    //测试类
    public class Main {
    
        public static void main(String[] args) {
            PizzaStore store = new PizzaStore();
            Pizza pizza = store.orderPizza("A");
    
        }
    }
    

    运行测试类,可以看到如下结果:

    A bake
    A cut
    

    符合我们的预期,但是如果我们有很多个商店,而且这些商店订购披萨的逻辑都是根据类型来判断(注意各个商店卖的Pizza不一定相同,所以往往不能抽到基类中去),即上述的if-else if ..结构。那么如果现在不打算生产A类型的Pizza了,取而代之的是C类型Pizza,我们就不得不到每个和A类型Pizza相关的商店代码里更改。

    现在用简单工厂模式来改写一下代码,将if-else结构抽到工厂类里。如下代码所示:

    //简单工厂
    public class SimplePizzaFactory {
    
        public Pizza createPizza(String type) {
            
            if ("A".equals(type)) {
                return new APizza();
            } else if ("B".equals(type)) {
                return new BPizza();
            }
            return null;
        }
    }
    
    //修改后的Pizza商店
    public class PizzaStore {
    
        private SimplePizzaFactory factory;
    
        public PizzaStore(SimplePizzaFactory factory) {
            this.factory = factory;
        }
    
        public Pizza orderPizza(String type) {
            Pizza pizza = this.factory.createPizza(type);
            if (pizza != null) {
                pizza.bake();
                pizza.cut();
            }
            return pizza;
        }
    }
    

    其余类不做修改,这样做的好处是将变化的部分抽到一个单独的类中,Pizza商店类仅仅依赖工厂的实现,而不依赖Pizza的具体实现,可以简单理解成Pizza商店不再自己生产Pizza,而是需要什么样的Pizza就到一个工厂中去“取货”即可。

    现在会过来分析一下如果需求变化了会发生什么?如果某个Pizza商店不需要A类型Pizza了(可能是因为不好卖),Pizza该怎么做呢?在我们的代码里,他什么也不用做!不需要修改任何代码即可,这是因为生产Pizza的责任交由Pizza工厂来管理了,客人需要什么样的Pizza,商店直接去Pizza工厂拿就行了,不关心Pizza具体是怎样的。

    不过这种方法也有局限性,例如现在有很多商店,这些商店可能想要一些自己的特色,不想到工厂里获取(注意,工厂也可以有很多种,商店可以任意选择到哪个工厂获取),该怎么办?一种解决方案是利用工厂方法模式

    工厂方法模式

    工厂方法模式和简单工厂的区别是,生产产品的地方不是类,而是在一个方法里,如下代码所示:

    public abstract class PizzaStore {
    
    
    
        public Pizza orderPizza(String type) {
    
            Pizza pizza = createPizza(type);
    
            if (pizza != null) {
                pizza.bake();
                pizza.cut();
            }
            return pizza;
        }
    
        protected abstract Pizza createPizza(String type);
    }
    
    //注意这里的createPizza(String type);是抽象方法。
    
    public class NYPizzaStore extends PizzaStore {
    
        @Override
        protected Pizza createPizza(String type) {
            if (type.equals("A")) {
                return new APizza();
            }
            return null;
        }
    }
    
    public class CHPizzaStore extends PizzaStore {
        @Override
        protected Pizza createPizza(String type) {
            if (type.equals("B"))
                return new BPizza();
            return null;
        }
    }
    
    //测试类
    public class Main {
    
        public static void main(String[] args) {
            PizzaStore pizzaStore = new NYPizzaStore();
            pizzaStore.orderPizza("A");
        }
    }
    

    可以看到上述代码不再使用单独的工厂类了,具体的生产Pizza的责任交由每个商店自己处理,自己想要干嘛就干嘛,而且自己负责。

    抽象工厂模式

    现在各个商店还不满足,他们发现客人对原料的要求很高,所以他们想自己去拿原料,各地的原料的质量又不一样,那到哪拿呢?怎么拿呢?这种情况可以用抽象工厂模式解决,引入一个抽象工厂,其子类是具体的工厂,然后各个商店决定从哪个工厂拿材料。如下代码所示:

    public interface PizzaFactory {
    
        void createSourceA();
        void createSourceB();
        void createSourceC();
    }
    
    public class APizzaFactory implements PizzaFactory {
        @Override
        public void createSourceA() {
            System.out.println("A create source A");
        }
    
        @Override
        public void createSourceB() {
            System.out.println("A create source B");
        }
    
        @Override
        public void createSourceC() {
            System.out.println("C create source C");
        }
    }
    
    //B工厂的代码和A工厂差不多,不贴了。
    
    //修改后的商店
    public class NYPizzaStore extends PizzaStore {
    
        @Override
        protected Pizza createPizza(String type) {
    
            PizzaFactory pizzaFactory = new APizzaFactory();
    
            if (type.equals("A")) {
                return new APizza(pizzaFactory);
            }
            return null;
        }
    }
    
    //修改后的A类型Pizza
    public class APizza extends Pizza {
    
        private PizzaFactory pizzaFactory;
    
        public APizza(PizzaFactory pizzaFactory) {
            this.pizzaFactory = pizzaFactory;
            this.name = "A Pizza";
        }
    
        @Override
        public void bake() {
            this.pizzaFactory.createSourceA();
            this.pizzaFactory.createSourceB();
            this.pizzaFactory.createSourceC();
            System.out.println("A bake");
        }
    
        @Override
        public void cut() {
            System.out.println("A cut");
        }
    }
    
    //测试类
    public class Main {
    
        public static void main(String[] args) {
            PizzaStore pizzaStore = new NYPizzaStore();
            pizzaStore.orderPizza("A");
    
        }
    }
    

    这样一来,各个商店可以自己决定从哪个工厂拿原料来生成Pizza了。

    小结

    工厂模式主要有三种:

    1. 简单工厂(常见的静态工厂也属于简单工厂)。
    2. 工厂方法。
    3. 抽象工厂。

    简单工厂很简单直观,就是将生产产品的逻辑抽离到一个单独的类中做管理,需要的时候就去拿。但是有局限性,即不够灵活。

    工厂方法的灵活性比简单工厂要高,通过继承,子类可以自行决定生产的逻辑。

    抽象工厂和工厂方法的区别不是那么明显,比较直观的区别是工厂方法是通过继承来做的,抽象工厂主要是通过组合来实现。抽象工厂的缺点是扩展需要新增子类,而工厂方法仅需要实现方法的具体逻辑即可,不过抽象工厂的结构会更加明显,易于阅读。总之,这两种方式效果差不多,具体选择哪种,还得看实际情况决定。

    下面是工厂模式的定义:

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

    抽象工厂模式: 抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。

    相关文章

      网友评论

        本文标题:设计模式笔记(四): 工厂模式

        本文链接:https://www.haomeiwen.com/subject/jpydmftx.html