【设计模式-工厂模式】从女娲造人看工厂模式

  • 代码
  • 工厂方法的定义

    神话中,女娲创造出了人类,而人类的人种大致可分为3类:黄、白、黑
    那么女娲是如何创造出这些不同的人种的人类?

    假设女娲有一个炉子,女娲只需要将捏成人形的泥土放入其中,就可以根据不同的火候得到不同肤色的人种,这大致就就是女娲造人的过程。

    在上面这段描述中,女娲其实就是一个场景类,负责业务逻辑的处理
    炉子则是一个工厂类,根据 女娲的需要可以创造出不同肤色的人种
    不同肤色的人种则是具体的产品类,他们都继承自同一个 抽象产品类 人类,而每个人有自己的肤色 以及有说话的能力

    类图如下所示,为了更完整的说明工厂模式,现将女娲使用的炉子也抽象出来(女娲有不同的炉子,可以创造不同的生物)

    女娲造人类图

    image-20200811134948198
    image-20200811134948198

    代码

    类图已经画出,那么试着用代码描述

    首先我们需要先将产品(不同的产品)抽象出一个抽象产品(抽象类或者接口)。在本例中 从各色人种中抽象出来的是一个人类接口

    public interface Human {
        //每个人种的皮肤都是不同的颜色
        public void getColor();
        //每个人类都会发声
        public void talk();
    }
    

    女娲创造出不同的人种都应该实现该接口

    人种

    黑色人种:

    /**
     * 黑色人种
     * @author LiPeng01
     * @since 2020/8/2 5:43 下午
     */
    public class BlackHuman implements Human {
        @Override
        public void getColor() {
            System.out.println("黑皮肤");
    
        }
    
        @Override
        public void talk() {
            System.out.println("非洲语");
        }
    }
    

    黄色人种

    package com.example.springboot.factory;
    
    /**
     * 黄色人种
     * @author LiPeng01
     * @since 2020/8/2 5:43 下午
     */
    public class YellowHuman implements Human {
        @Override
        public void getColor() {
            System.out.println("黄皮肤");
    
        }
    
        @Override
        public void talk() {
            System.out.println("国语");
    
        }
    }
    

    白色人种:

    package com.example.springboot.factory;
    
    /**
     * 白色人种
     * @author LiPeng01
     * @since 2020/8/2 5:43 下午
     */
    public class WhiteHuman implements Human
    {
        @Override
        public void getColor() {
            System.out.println("白皮肤");
    
        }
    
        @Override
        public void talk() {
            System.out.println("英语");
    
    
        }
    }
    

    炉子

    确认完我们需要创建的人种后,我们需要个炉子能够烧制泥人为我们所需要的人种。
    该炉子对使用者女娲来说,应该是一个黑盒。只需要女娲告诉炉子 她需要什么人种,炉子就可以自动创建出来该人种的一个人类,不需要女娲去知道炉子内部是怎么烧制的。

    我们定义一个抽象类,该类的抽象方法是用于生产人类的,这里使用了泛型来接受不同人种

    /**
     * 抽象工厂(八卦炉)
     * @author LiPeng01
     * @since 2020/8/2 5:58 下午
     */
    public abstract class AbstractHumanFactory {
        /**
         * 生产人类的方法
         * @param c
         * @param <T>
         * @return
         */
        public abstract <T extends Human> T createHuman(Class<T> c);
    }
    

    炉子有各种各样的,女娲目前需要的是一个能够烧制人类的炉子,所以我们需要创建个实现了抽象类的 阴阳八卦炉用于创建人类实例

    package com.example.springboot.factory;
    
    /**
     * @author LiPeng01
     * @since 2020/8/2 6:01 下午
     */
    public class HumanFactory extends AbstractHumanFactory {
        @Override
        //T就是 Class<T>中的T, 先有Class<T>的定义,才能在这个Class中使用T作为泛型定义
        public <T extends Human> T createHuman(Class<T> c) {
            //定义一个生产的人种
            Human human = null;
            try{
                //产生一个人种
                human = (T)Class.forName(c.getName()).newInstance();
                //            human = c.newInstance();
            } catch (Exception e) {
                System.out.println("人种生成错误!");
            }
            return (T)human;
        }
    }
    

    女娲

    到目前为止,造人的工具以及需要造的人的类型我们都已经获得了,现在可以让女娲正式开始创建人类。女娲不需要知道炉子内部是怎么实现的,她只需要告诉炉子她所想要的人种,炉子就会自动为她创建一个实例。

    package com.example.springboot.factory;
    
    /**
     * @author LiPeng01
     * @since 2020/8/2 10:35 下午
     */
    public class NvWa {
        public static void main(String[] args) {
            //声明一个八卦炉
            AbstractHumanFactory yinYangLu = new HumanFactory();
            //女娲第一次造人.火候不足,产生了白人
            System.out.println("--制造白人--");
            Human whiteHuman = yinYangLu.createHuman(WhiteHuman.class);
            whiteHuman.getColor();
            whiteHuman.talk();
            //第二次造人 火候过了,产生了黑人
            System.out.println("--制造黑人--");
            Human blackHuman = yinYangLu.createHuman(BlackHuman.class);
            blackHuman.getColor();
            blackHuman.talk();
    
            //第三次造人,火候刚好,产生了黄人
            System.out.println("--黄种人--");
            Human yellowHuman = yinYangLu.createHuman(YellowHuman.class);
            yellowHuman.getColor();
            yellowHuman.talk();
    
        }
    }
    

    运行结果:

    --制造白人--
    白皮肤
    英语
    --制造黑人--
    黑皮肤
    非洲语
    --黄种人--
    黄皮肤
    国语

    工厂方法的定义

    Define an interface for creating an object,but let subclasses decide whichclass to instantiate.Factory Method lets a class defer instantiation tosubclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)

    通用类图

    工厂模式通用类图
    工厂模式通用类图

    在工厂方法模式中,抽象产品类Porduct 负责定义产品的共性,实现对事物最抽象的定义; Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类则有具体的ConcreteCreator负责完成。

    以女娲为例: 人类就是一个抽象产品类,它定义了人类的共性:说话和肤色;炉子则是一个抽象工厂,他的具体实现阴阳八卦炉则是专门用于创建人类的,而具体的创建规则则是在阴阳八卦炉里定义的。本例中就是根据场景传来的名称来决定。

    好处

    使用工厂类的好处是什么呢?

    首先,工厂类实现了对代码的良好的封装,使得一个对象的创建得到约束。如果调用者需要得到一个具体的产品对象,他只需要告诉工厂类 目标对象的产品类名称即可得到该类实例,而对象的创建过程对调用者来说是透明的,也就降低了模块之间的耦合。

    其次,拓展性得到了提升,如果需求变更需要新增加个肤色人种(如:棕色肤色) ,那么只需要在新增加个产品类即可。甚至哪天说想要把原来场景类中使用的人种换个肤色,也只需要改动下场景类的类名即可得到新人种的对象。这种在增加产品类的情况下,只需要适当的修改具体的工厂类或者扩展一个工厂类,就可以拥抱变化。

    再次,使用工厂模式,使得场景类屏蔽了产品类。产品类的实现如何变化,调用者都不需要关注,只需要抽象产品类的接口不变化,那么调用者就不用变化。因为具体的实现是由工厂类负责。黑色人种也可以说国语,但是对调用者来说,这些变化都是对它没有任何影响的。

    最后,工厂方法模式是典型的解耦框架。高层模块值需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则,我不需要的就不要去交流;也符合依赖倒置原则,只依赖产品类的抽象;当然也符合里氏替换原则,使用产品子类替换产品父类,没问题!

  • posted @ 2021-05-14 11:09  澎拜编程  阅读(525)  评论(0)    收藏  举报