zoukankan      html  css  js  c++  java
  • 03-肯德基点餐:抽象工厂模式

    3.1模式关联的故事背景

      去肯德基点餐(一个麻辣鸡腿汉堡、四个奥尔良烤鸡翅、一包薯条、两杯可乐)

    3.2模式定义

      抽象工厂模式(Abstract Factory Pattern)提供了一个接口,用于创建相关或者依赖对象的家族,而不需要指定具体的实现类。

      抽象工厂模式允许客户使用抽象接口来创建一组相关的产品,客户类和工厂类分开,客户需要任何产品的时候,只需要向工厂请求即可,客户无须修改就可以获得新产品。这样一来,客户就从具体产品中解耦。

    3.3故事中的模式分析

    3.3.1故事中的角色

      肯德基店---生产食物的工厂

      食物(汉堡、鸡翅、薯条、可乐)---工厂生产的产品

      客户---食物购买者

      以上三种角色的关系如图3-2所示:

      

    3.3.2抽象化分析

      还记得“开-闭”原则和“依赖倒置”原则吗?这是我们进行软件程序设计的指导原则。我们要让程序对扩展开放,对修改关闭,如何能做到呢?——抽象!我们要对系统模型进行最大化的抽象,才能达到“开-闭”原则的要求,又如何才能做到最大抽象呢?“依赖倒置”原则为我们指明了一条方向,从具体的类型来进行抽象。

      来看下故事中出现的对象:肯德基店就是一个具体的工厂,这时,我们需要抽象一个工厂,在抽象工厂中指明了生产各种抽象食物的方法,如生产汉堡、鸡翅等,肯德基店就需要实现这个抽象工厂,生产具体的食品,如生产麻辣鸡腿汉堡、生产奥尔良鸡翅等。前面提到了“抽象食物”,我们还需要对每个具体的食物添加抽象父类,如汉堡就是抽象父类,麻辣鸡腿汉堡就是汉堡的一个子类,依次类推。这时,我们会发现,每一种食物又都存在着一些共同的属性,如风味、单价、数量等,因此,我们继续进行抽象,所有的抽象食物都继承一个抽象父类。客户如何订购肯德基生产的食物呢?这与上一章的工厂方法模式有所不同,这里我们使用组合的方式,将抽象工厂作为客户类中的一个实例变量,客户需要任何产品的时候,只需要向工厂请求即可,这就是抽象工厂模式的应用方式。客户类和工厂类分开,客户无须修改就可以获得新产品。

      通过以上分析,对图3-2进行抽象化改进,如图3-3(a)所示。

      

      看似改动很小,但是意义深远。我们在客户类中使用的都是抽象类和接口,这样即使生产再多的食物都不用担心了。

    3.3.3抽象工厂模式的静态建模

      现在客户——肯德基——食物三者之间的关系已经理顺了,静态类图如3-3(b)所示。

      图中所表达的内容是客户需要食物只要向抽象工厂请求即可,由具体生产具体产品给客户。

      

    3.4故事抽象工厂模式的实现

    3.4.1抽象食物的建立

    1)抽象食物——AbstractBaseFood

    package com.demo.factory.model;
    
    /**
     * Created by lsq on 2018/3/13.
     * 食物基类
     */
    public abstract class AbstractBaseFood {
    
        //类别
        protected String kind;
        //数量
        protected int num;
        //价格
        protected float price;
    
        //合计
        public float totalPrice(){
            return this.num * this.price;
        }
    
    }

    2)食物接口——IFood

      新建IFood接口,该接口存在一个printMessage方法,子类实现该方法打印输出食物的相关信息。

    package com.demo.factory.model;
    
    /**
     * Created by lsq on 2018/3/13.
     * 抽象食物接口
     */
    public interface IFood {
    
        /**
         * 打印输出食物信息
         */
        void printMessage();
    
    }

    3.4.2建立不同食物的抽象基类

      每种食物的抽象类都继承AbstractBaseFood基类,实现IFood接口。每种食物的抽象基类很简单,就是实现IFood的printMessage方法。

    1)汉堡基类——Hamburg

    package com.demo.factory.model;
    
    /**
     * Created by lsq on 2018/3/13.
     *  汉堡基类
     */
    public abstract class Hamburg extends AbstractBaseFood implements IFood{
    
        public void printMessage(){
            System.out.println("--"+this.kind+"风味汉堡,	单价:"+this.price+
                    ",	数量:"+this.num+",	合计:"+this.totalPrice());
        }
    
    }

    2)鸡翅基类——ChickenWings

    package com.demo.factory.model;
    
    /**
     * Created by lsq on 2018/3/13.
     * 鸡翅基类
     */
    public abstract class ChickenWings extends AbstractBaseFood implements IFood{
    
        public void printMessage(){
            System.out.println("--"+this.kind+"风味鸡翅,	单价:"+this.price+
                    ",	数量:"+this.num+",	合计:"+this.totalPrice());
        }
    
    }

    3)薯条基类——FrenchFries

    package com.demo.factory.model;
    
    /**
     * Created by lsq on 2018/3/13.
     * 薯条基类
     */
    public abstract class FrenchFries extends AbstractBaseFood implements IFood{
    
        public void printMessage(){
            System.out.println("--"+this.kind+"风味薯条,	单价:"+this.price+
                    ",	数量:"+this.num+",	合计:"+this.totalPrice());
        }
    
    }

    4)饮料基类——Beverage

    package com.demo.factory.model;
    
    /**
     * Created by lsq on 2018/3/13.
     * 饮料基类
     */
    public abstract class Beverage extends AbstractBaseFood implements IFood{
    
        public void printMessage(){
            System.out.println("--"+this.kind+"饮料,	单价:"+this.price+
                    ",	数量:"+this.num+",	合计:"+this.totalPrice());
        }
    
    }

    3.4.3创建具体的食物

      每个具体食物实现类也很简单,采用由一个数量作为参数的构造方法,类别和单价在每种食物中都已经指明。

    1)麻辣鸡腿汉堡——ChinaHamburg

      麻辣鸡腿汉堡实现类ChinaHamburg,继承汉堡基类Hamburg。

    package com.demo.factory.model.kfc;
    
    import com.demo.factory.model.Hamburg;
    
    /**
     * Created by lsq on 2018/3/13.
     * 中国风味的麻辣鸡腿汉堡
     */
    public class ChinaHamburg extends Hamburg{
    
        /**
         * 构造方法
         * @param num
         */
        public ChinaHamburg(int num) {
            this.kind = "麻辣";
            this.price = 14.0f;
            this.num = num;
        }
    }

    2)奥尔良烤鸡翅——ChinaChickenWings

    package com.demo.factory.model.kfc;
    
    import com.demo.factory.model.ChickenWings;
    
    /**
     * Created by lsq on 2018/3/13.
     * 鸡翅实现类
     */
    public class ChinaChickenWings extends ChickenWings{
    
        /**
         * 构造方法
         * @param num
         */
        public ChinaChickenWings(int num) {
            this.kind = "奥尔良";
            this.price = 2.5f;
            this.num = num;
        }
    }

    3)薯条——ChinaFrenchFries

    package com.demo.factory.model.kfc;
    
    import com.demo.factory.model.FrenchFries;
    
    /**
     * Created by lsq on 2018/3/13.
     * 薯条实现类
     */
    public class ChinaFrenchFries extends FrenchFries{
    
        public ChinaFrenchFries(int num) {
            this.kind = "普通";
            this.price = 8.0f;
            this.num = num;
        }
    }

    4)可乐——ChinaBeverage

    package com.demo.factory.model.kfc;
    
    import com.demo.factory.model.Beverage;
    /**
     * Created by lsq on 2018/3/13.
     * 薯条实现类
     */
    public class ChinaBeverage extends Beverage{
    
        public ChinaBeverage(int num) {
            this.kind = "可乐";
            this.price = 7.0f;
            this.num = num;
        }
    }

    3.4.4建立工厂

    1)创建抽象肯德基工厂——IKfcFactory生产抽象食物

      产品已经有了,下面建立生产产品的抽象工厂。

    package com.demo.factory.itf;
    
    import com.demo.factory.model.Beverage;
    import com.demo.factory.model.ChickenWings;
    import com.demo.factory.model.FrenchFries;
    import com.demo.factory.model.Hamburg;
    
    /**
     * Created by lsq on 2018/3/13.
     * 肯德基抽象工厂
     */
    public interface IKfcFactory {
    
        //生产汉堡
        public Hamburg createHamburg(int num);
    
        //生产薯条
        public FrenchFries createFrenchFries(int num);
    
        //生产鸡翅
        public ChickenWings createChickenWings(int num);
    
        //生产饮料
        public Beverage createBeverage(int num);
    
    }

      注意:抽象工厂中创建各种食物,都是抽象食物。

    2)创建具体肯德基工厂——ChinaKfcFactory生产具体食物

    package com.demo.factory.itf;
    
    import com.demo.factory.model.Beverage;
    import com.demo.factory.model.ChickenWings;
    import com.demo.factory.model.FrenchFries;
    import com.demo.factory.model.Hamburg;
    import com.demo.factory.model.kfc.ChinaBeverage;
    import com.demo.factory.model.kfc.ChinaChickenWings;
    import com.demo.factory.model.kfc.ChinaFrenchFries;
    import com.demo.factory.model.kfc.ChinaHamburg;
    
    /**
     * Created by lsq on 2018/3/13.
     * 具体工厂
     */
    public class ChinaKfcFactory implements IKfcFactory{
    
        //生产汉堡
        public Hamburg createHamburg(int num) {
            return new ChinaHamburg(num);
        }
    
        //生产薯条
        public FrenchFries createFrenchFries(int num) {
            return new ChinaFrenchFries(num);
        }
    
        //生产鸡翅
        public ChickenWings createChickenWings(int num) {
            return new ChinaChickenWings(num);
        }
    
        //生产饮料
        public Beverage createBeverage(int num) {
            return new ChinaBeverage(num);
        }
    }

    3.4.5创建客户类

      客户类中含有一个抽象工厂IKfcFactory类型的实例变量kfcFactory,客户类Customer通过构造方法将肯德基店实例传入,客户需要食物时,就向肯德基店(工厂)请求,客户不生产食物(不使用new生成对象)。

    package com.demo.factory.custom;
    
    import com.demo.factory.itf.IKfcFactory;
    import com.demo.factory.model.Beverage;
    import com.demo.factory.model.ChickenWings;
    import com.demo.factory.model.FrenchFries;
    import com.demo.factory.model.Hamburg;
    
    /**
     * Created by lsq on 2018/3/13.
     * 客户类
     */
    public class Customer {
    
        //抽象工厂
        private IKfcFactory kfcFactory;
    
        //构造方法将抽象工厂作为参数传入
        public Customer(IKfcFactory kfcFactory){
            this.kfcFactory = kfcFactory;
        }
    
        /**
         * 订购食物
         */
        //订购麻辣鸡腿汉堡
        public float orderHamburg(int num){
            //获得麻辣鸡腿汉堡
            Hamburg hamburg = kfcFactory.createHamburg(num);
            //输出订购信息
            hamburg.printMessage();
            //返回总价
            return hamburg.totalPrice();
        }
    
        //订购奥尔良烤鸡翅
        public float orderChickenWings(int num){
            //获得奥尔良烤鸡翅
            ChickenWings chickenWings = kfcFactory.createChickenWings(num);
            //输出订购信息
            chickenWings.printMessage();
            //返回总价
            return chickenWings.totalPrice();
        }
    
        //订购薯条
        public float orderFrenchFries(int num){
            //获得薯条
            FrenchFries frenchFries = kfcFactory.createFrenchFries(num);
            //输出订购信息
            frenchFries.printMessage();
            //返回总价
            return frenchFries.totalPrice();
        }
    
        //订购可乐
        public float orderBeverage(int num){
            //获得可乐
            Beverage beverage = kfcFactory.createBeverage(num);
            //输出订购信息
            beverage.printMessage();
            //返回总价
            return beverage.totalPrice();
        }
    
    }

    3.4.6客户到店点餐

    import com.demo.factory.custom.Customer;
    import com.demo.factory.itf.ChinaKfcFactory;
    import com.demo.factory.itf.IKfcFactory;
    
    /**
     * Created by lsq on 2018/3/13.
     *
     */
    public class MainApp {
    
        public static void main(String[] args) {
    
            /**
             * 定义一个肯德基(IKfcFactory类型)
             */
            IKfcFactory kfcFactory = new ChinaKfcFactory();
    
            /**
             * 创建客户
             */
            Customer customer = new Customer(kfcFactory);
    
            /**
             * 客户开始点餐
             */
            //一个麻辣鸡腿汉堡
            float hamburgMoney = customer.orderHamburg(1);
            //四个奥尔良烤鸡翅
            float chickenWingsMoney = customer.orderChickenWings(4);
            //一包薯条
            float frenchFriesMoney = customer.orderFrenchFries(1);
            //两杯可乐
            float beverageMoney = customer.orderBeverage(2);
    
            System.out.println("总计:"+(hamburgMoney+chickenWingsMoney+frenchFriesMoney+beverageMoney));
        }
    
    }

      运行结果:

    3.6使用场合

    1)创建产品家族,相关产品集合在一起使用的时候;

    2)想要提供一个产品类库,并只想显示其接口而不是实现时;

    3)通过组合的方式使用工厂时。

      抽象工厂模式提供了一个接口,用于创建相关或者依赖对象的家族,而不需要指定具体实现类。抽象工厂模式是指当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使用客户端在不必指定具体产品的情况下,创建多个产品族中的产品对象。当有多个抽象产品角色时,工厂方法模式已经不能满足要求。

      在抽象工厂模式中,定义了一个抽象接口,如本例中的IKfcFactory,之后具体工厂又通过实现抽象工厂的各种接口方法创建实例对象,如本例中的ChinaKfcFactory,这不正是之前讲的工厂方法模式吗?的确,在抽象工厂模式中使用了工厂方法模式的实现方式。

    3.7抽象工厂模式和工厂方法模式的区别

    1)工厂方法模式通过继承的方式实现应用程序的解耦,而抽象工厂模式则通过对象组合的方式实现应用程序的解耦;

    2)工厂方法模式用来创建一个抽象产品,具体工厂实现工厂方法来创建具体产品,而抽象工厂模式用来创建一个产品家族的抽象类型。

  • 相关阅读:
    kmp dp hdu 3336
    Python之路【第三篇】:Python基础(18)——函数万能参数
    Python之路【第三篇】:Python基础(17)——函数动态参数
    Python之路【第三篇】:Python基础(16)——函数动态参数
    Python之路【第三篇】:Python基础(15)——函数指定参数
    Python之路【第三篇】:Python基础(13)——函数普通参数
    Python之路【第三篇】:Python基础(14)——函数默认参数
    Python之路【第三篇】:Python基础(12)——函数
    Python之路【第三篇】:Python基础(11)——set集合
    Python之路【第三篇】:Python基础(10)——set集合
  • 原文地址:https://www.cnblogs.com/danielleee/p/8567124.html
Copyright © 2011-2022 走看看