• HeadFirst设计模式读书笔记之工厂模式


    1. 简单工厂

    1. 你开了一家披萨店,点披萨的方法可能是这样:

        public Pizza orderPizza(String type) {
            Pizza pizza;
            if (type.equals("芒果披萨")){
                pizza = new MangoPizza();
            }else if (type.equals("核桃披萨")){
                pizza = new WalnutPizza();
            }else if (type.equals("橙皮披萨")){
                pizza = new FlavedoPizza();
            }else {
                pizza = new StarPizza();
            }
    
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }
    

    可以看到,每当你想增加一种披萨类型,就要修改代码,添加一种if else条件.当有多个系统存在orderPizza的需求时,每个系统都要同时修改他们的代码.因此,需要将这种实例化具体对象的代码封装起来.

    public class PizzaStore {
    
        SimplePizzaFactory factory;
    
        public PizzaStore(SimplePizzaFactory factory){
            this.factory = factory;
        }
    
        public Pizza orderPizza(String type) {
            Pizza pizza;
            pizza = factory.createPizza(type);
    
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }
    }
    
    public class SimplePizzaFactory {
    
        public Pizza createPizza(String type){
            Pizza pizza = null;
            
            if (type.equals("芒果披萨")){
                pizza = new MangoPizza();
            }else if (type.equals("核桃披萨")){
                pizza = new WalnutPizza();
            }else if (type.equals("橙皮披萨")){
                pizza = new FlavedoPizza();
            }else {
                pizza = new StarPizza();
            }
            
            return pizza;
        }
    }
    

    这就是简单工厂方法,他不算一种设计模式,而是一种编程习惯.其要点有二:

    1. 将产品定义为抽象类,可以被覆盖

    2. 将具体的产品放到工厂类中来实例化,由工厂创建并返回给客户,这样便使得产品的创建逻辑可以解耦合,并增加了复用性

    2. 工厂方法

    1. 假设你现在有了很多加盟店,每种加盟店根据地区的差异有自己的工厂,但加盟店虽然采用工厂方法创建披萨,但其他部分却有所不同:烘烤的做法,不切片,使用杂七杂八的盒子...

    这时,你想把创建披萨的方法与每家披萨店绑定在一起,让每家PizzaStore中都使用createPizza()创建pizza,并且这些店还可以拥有一定的制作披萨的自主权.因此,我们需要把PizzaStore和createPizza()定义成抽象的

    /**
     * 抽象的PizzaStore
     */
    public abstract class PizzaStore {
    
        public Pizza orderPizza(String type) {
            Pizza pizza;
            // 使用自己的方法做披萨
            pizza = createPizza(type);
    
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }
    
        // 将做披萨的工厂方法放到PizzaStore中,工厂方法是抽象的,所以依赖子类来处理对象的创建
        public abstract Pizza createPizza(String type);
    }
    

    2. 现在由PizzaStore的子类来具体卖披萨

    public class BeijingPizzaStore extends PizzaStore {
        @Override
        public Pizza createPizza(String item) {
            if (item.equals("橙皮披萨")){
                return new FlavedoPizza();
            }else {
                return new PekingDuckPizza();
            }
        }
    }
    
    public class ChengduPizza extends PizzaStore {
        @Override
        public Pizza createPizza(String type) {
            if (type.equals("芒果披萨")){
                return new MangoPizza();
            }else {
                return new StarPizza();
            }
        }
    }
    

    现在,orderPizza()与具体的Pizza实现了一定的解耦合,决定最终Pizza对象的变成了某个具体的pizza店,而pizza店则由顾客来决定

    3. 一些对工厂方法的理解:

    1. 工厂方法是抽象的,所以依赖子类来创建对象,这样便将超类的代码与具体创建产品的代码分割开了

    2. 工厂方法必须返回一个产品,超类通常中会用到这个产品

    3. 工厂方法将产品与创建者抽象为接口,使他们成为平级的存在,具体的产品创建则使用子类来交互

    4. 工厂方法将产品实例化的操作推迟到子类,子类的选择则由创建者来决定

    5. 简单工厂方法更趋向组合,工厂方法则利用抽象和继承

    6. 简单工厂封装了对象的创建,一个工厂处理所有的产品,但对同一个产品不可变更其做法,工厂方法则更有弹性,子类通过重写可以更改产品的创建

    3. 依赖倒置原则

    下面是一个简单粗暴的披萨店:

    /**
     * 依赖具体对象的PizzaStore
     */
    public class PizzaStore {
        
        public Pizza createPizza(String style, String type){
            Pizza pizza = null;
            if (style.equals("一元档")){
                if (type.equals("芒果披萨")){
                    pizza = new MangoPizza();
                }else if (type.equals("核桃披萨")){
                    pizza = new WalnutPizza();
                }
            }else if (style.equals("二元档")){
                if (type.equals("北京烤鸭披萨")){
                    pizza = new PekingDuckPizza();
                }else if (type.equals("橙皮披萨")){
                    pizza = new FlavedoPizza();
                }
            }else {
                System.out.println("披萨卖完了");
            }
            
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }
    }
    

    可以看到,这个披萨店依赖于每一个具体的披萨对象,每增加一个披萨就要修改一次代码,增加一个依赖,可以说是非常不好用了

    从图中看到,高层组件直接依赖于低层组件而依赖倒置原则的的内容就是:

    要依赖抽象,不要依赖具体类
    这个原则说明不管高层还是低层组件都不应该依赖具体类,而应该依赖于抽象.如图所示:

    与之前相比高层组件PizzaStore不在直接依赖于具体的Pizza对象,而是依赖于Pizza接口,而具体的Pizza产品则由被高层组件依赖颠倒成了依赖Pizza接口,这就是一种依赖倒置.总体来看,PizzaStore和Pizza都抽象化了,不用再依赖于具体的类型,符合依赖倒置的设计原则.
    有三个指导方针可以让你更好地遵守依赖倒置原则:

    • 变量不可以持有具体类的引用
    • 不要让类派生自具体类
    • 不要覆盖基类中已覆盖的方法

    4. 抽象工厂方法:创建产品家族

    与普通的工厂方法比较,抽象工厂方法要显得重型得多: 工厂方法只创建一种产品,抽象工厂方法则创建一个家族的产品

    1. 典型做法是创建一个工厂的接口,这个接口负责创建所有的产品家族成员,如创建一个生产调料家族的工厂:

    /**
    * 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料
    * 每个原料都是一个类
    */
    public interface SeasoningFactory {
    
       Chili createChili();
       
       DouBan createDouBan();
       
       Pepper createPepper();
       
       Salt createSalt();
    
    }
    
    /**
     * 郫县调料生产厂
     */
    public class PiXianSeasoningFactory implements SeasoningFactory {
    
        /**
         *  虎皮青椒
         * @return
         */
        @Override
        public Chili createChili() {
            return new HuPiQingJiao();
        }
    
        /**
         * 郫县豆瓣
         * @return
         */
        @Override
        public DouBan createDouBan() {
            return new PiXianDouBan();
        }
    
        /**
         * 花椒油
         * @return
         */
        @Override
        public Pepper createPepper() {
            return new HuaJiaoYou();
        }
    
        /**
         * 食用盐
         * @return
         */
        @Override
        public Salt createSalt() {
            return new ShiYongYan();
        }
    }
    
    
    /**
     * 灌县调料生产厂
     */
    public class GuanXianSeasoningFactory implements SeasoningFactory {
    
        /**
         *  虎皮青椒
         * @return
         */
        @Override
        public Chili createChili() {
            return new HuPiQingJiao();
        }
    
        /**
         * 老干妈豆瓣酱
         * @return
         */
        @Override
        public DouBan createDouBan() {
            return new LaoGanMaDouBanJiang();
        }
    
        /**
         * 花椒油
         * @return
         */
        @Override
        public Pepper createPepper() {
            return new HuaJiaoYou();
        }
    
        /**
         * 食用盐
         * @return
         */
        @Override
        public Salt createSalt() {
            return new ShiYongYan();
        }
    }
    

    下面是抽象工厂方法的类图:

    从类图中可以看出来,抽象工厂方法有一个缺点,就是当添加了一个新产品接口时,要去抽象工厂接口中添加一个方法,这会造成一些麻烦

    2. 工厂方法与抽象工厂的一些对比

    相同点:

    • 都是负责创建对象
    • 都能使程序解耦,将程序从创建大量具体对象的泥潭中拉出来

    不同点:

    • 工厂方法用子类来创建具体对象,利用抽象和继承.抽象工厂包含了一大堆接口,利用组合的思想来创建对象
    • 工厂方法一般用来创建一种产品,抽象工厂则创建一族产品
    • 工厂方法功能简单,使用轻便.抽象工厂功能强大,使用时会创建大量的类

    3. 下面使用抽象工厂生产的原料来制作披萨

    /**
     * 原料工厂的接口,实现这样的接口,可以随意组合产出各式各样的调料
     * 每个原料都是一个类
     */
    public interface SeasoningFactory {
    
        /**
         * 辣椒
         * @return
         */
        Chili createChili();
    
        /**
         * 豆瓣
         * @return
         */
        DouBan createDouBan();
    
        /**
         * 花椒
         * @return
         */
        Pepper createPepper();
    
        /**
         * 盐
         * @return
         */
        Salt createSalt();
    
    }
    
    /**
     * 豆瓣
     */
    public abstract class DouBan {
        public abstract String sayMyName();
    }
    
    public class PiXianDouBan extends DouBan {
        @Override
        public String sayMyName() {
            return "郫县豆瓣";
        }
    }
    
    public class LaoGanMaDouBanJiang extends DouBan {
        @Override
        public String sayMyName() {
            return "老干妈豆瓣酱";
        }
    }
    
    /**
     * 郫县调料生产厂
     */
    public class PiXianSeasoningFactory implements SeasoningFactory {
    
        /**
         *  虎皮青椒
         * @return
         */
        @Override
        public Chili createChili() {
            return new HuPiQingJiao();
        }
    
        /**
         * 郫县豆瓣
         * @return
         */
        @Override
        public DouBan createDouBan() {
            return new PiXianDouBan();
        }
    
        /**
         * 花椒油
         * @return
         */
        @Override
        public Pepper createPepper() {
            return new HuaJiaoYou();
        }
    
        /**
         * 食用盐
         * @return
         */
        @Override
        public Salt createSalt() {
            return new ShiYongYan();
        }
    }
    
    /**
     * 灌县调料生产厂
     */
    public class GuanXianSeasoningFactory implements SeasoningFactory {
    
        /**
         *  虎皮青椒
         * @return
         */
        @Override
        public Chili createChili() {
            return new HuPiQingJiao();
        }
    
        /**
         * 老干妈豆瓣酱
         * @return
         */
        @Override
        public DouBan createDouBan() {
            return new LaoGanMaDouBanJiang();
        }
    
        /**
         * 花椒油
         * @return
         */
        @Override
        public Pepper createPepper() {
            return new HuaJiaoYou();
        }
    
        /**
         * 食用盐
         * @return
         */
        @Override
        public Salt createSalt() {
            return new ShiYongYan();
        }
    }
    
    /**
     * 每一个披萨都有一组抽象工厂生产的原料
     *
     */
    public abstract class Pizza {
    
        protected String name;
    
        /**
         * 辣椒
         */
        protected Chili chili;
    
        /**
         * 豆瓣
         */
        protected DouBan douBan;
    
        /**
         * 花椒
         */
        protected Pepper pepper;
    
        /**
         * 盐
         */
        protected Salt salt;
    
    
        /**
         * 在这个方法中,收集抽象工厂生产的原料
         */
        public abstract void prepare();
    
        public void bake(){
            System.out.println("烘烤...");
        }
    
        public void cut(){
            System.out.println("切片...");
        }
    
        public void box() {
            System.out.println("包装...");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString(){
             return "披萨制作完毕:"+name
            +"
    生产原料:"+chili.sayMyName()
                    +"	"+douBan.sayMyName()
                    +"	"+pepper.sayMyName()
                    +"	"+salt.sayMyName();
    
        }
    }
    
    public class ChengduPizza extends Pizza{
    
        // 调料工厂
        SeasoningFactory seasoningFactory;
    
        public ChengduPizza(SeasoningFactory seasoningFactory) {
            this.name = "旧成都披萨";
            this.seasoningFactory = seasoningFactory;
        }
    
        @Override
        public void prepare() {
            System.out.println("开始准备 "+getName()+":");
            chili = seasoningFactory.createChili();
            douBan = seasoningFactory.createDouBan();
            pepper = seasoningFactory.createPepper();
            salt = seasoningFactory.createSalt();
            System.out.println("以下原料准备完毕:"+chili.sayMyName()
            +","+douBan.sayMyName()+","+pepper.sayMyName()+","+salt.sayMyName());
        }
    }
    
    public abstract class PizzaStore {
    
        public abstract Pizza createPizza(String type);
    }
    
    public class StarBoundPizzaStore extends PizzaStore {
        @Override
        public Pizza createPizza(String type) {
            Pizza pizza;
            SeasoningFactory factoryA = new PiXianSeasoningFactory();
            SeasoningFactory factoryB = new GuanXianSeasoningFactory();
    
            if (type.equals("旧成都披萨")){
                pizza = new ChengduPizza(factoryA);;
            }else if (type.equals("新北京披萨")){
                pizza = new PekingPizza(factoryB);
            }else {
                pizza = new ChengduPizza(factoryB);
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
    
            return pizza;
        }
    
        public static void main(String[] args) {
            PizzaStore pizzaStore = new StarBoundPizzaStore();
            Pizza pizza = pizzaStore.createPizza("旧成都披萨");
            System.out.println(pizza.toString());
            Pizza pizza1 = pizzaStore.createPizza("新北京披萨");
            System.out.println(pizza1.toString());
        }
    }
    
    结果:
    开始准备 旧成都披萨:
    以下原料准备完毕:虎皮青椒,郫县豆瓣,花椒油,食用盐
    烘烤...
    切片...
    包装...
    披萨制作完毕:旧成都披萨
    生产原料:虎皮青椒	郫县豆瓣	花椒油	食用盐
    开始准备 新北京披萨:
    以下原料准备完毕:虎皮青椒,老干妈豆瓣酱,花椒油,食用盐
    烘烤...
    切片...
    包装...
    披萨制作完毕:新北京披萨
    生产原料:虎皮青椒	老干妈豆瓣酱	花椒油	食用盐
    
  • 相关阅读:
    随便说说
    郁闷
    请各栏目的负责人,开始整理自己栏目的文章
    祝博客园生日快乐
    Windows Live Writer中打开博客日志(最新版可以支持打开3000以内的日志)
    编译器优化对齐(字节对齐)
    HDlock 锁住硬盘的解决方式
    linux中env,export, set的区别
    System Volume Information 文件夹权限控制
    BOOL与bool的区别(bool不是c的关键字,c++中bool也不是int)
  • 原文地址:https://www.cnblogs.com/Lothlorien/p/10029930.html
Copyright © 2020-2023  润新知