• 创建型设计模式--工厂模式、抽象工厂模式


    一、小案例分析

    1、功能需求:

      实现一个发送信息的功能,要便于扩展与维护。
    (1)发送信息的工具有很多,比如短信、微信、邮件、QQ等。
    (2)选择某个工具进行信息发送。

    2、小菜鸡去实现:

    (1)定义一个发送工具的父类(接口),并将各种发送工具作为子类(实现类)。
    (2)定义一个选择发送工具的类,用于调用发送工具(直接new个子类对象)。
    (3)代码实现:

    package creative.pattern.factory.noFactory;
    
    import java.util.Scanner;
    
    /**
     * 测试类
     *
     */
    public class Demo {
        public static void main(String[] args) {
            new SendMessage();// 实例化一个选择发送工具的类
        }
    }
    
    /**
     * 定义一个发送工具的接口
     *
     */
    interface Sender {
        void sendMessage();
    }
    
    /**
     * 定义一个短信发送工具,实现接口,重写方法
     *
     */
    class ShortMessageSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送短信");
        }
    }
    
    /**
     * 定义一个微信发送工具,实现接口,重写方法
     *
     */
    class WeChatSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送微信");
        }
    }
    
    /**
     * 定义一个邮件发送工具,实现接口,重写方法
     *
     */
    class MailSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送邮件");
        }
    }
    
    /**
     * 定义一个选择发送工具的类
     */
    class SendMessage {
        /**
         * 用于获取需要发送信息工具
         */
        public Sender getSenderType() {
            System.out.println("输入发送工具的类型(1-3):");
            System.out.println("1:短信");
            System.out.println("2:微信");
            System.out.println("3:邮件");
            Scanner scanner = new Scanner(System.in);
            String senderType = scanner.nextLine();
            if ("1".equals(senderType)) {
                return new ShortMessageSender();
            } else if ("2".equals(senderType)) {
                return new WeChatSender();
            } else if ("3".equals(senderType)) {
                return new MailSender();
            } else {
                return null;
            }
        }
    
        public SendMessage() {
            do {
                Sender sender = getSenderType();// 选择发送信息的工具
                if (sender == null) {
                    System.out.println("欢迎下次使用");
                    break;
                } else {
                    sender.sendMessage();
                }
            } while (true);
        }
    }

    (4)代码分析:
      对于逻辑简单的代码,这样实现没有问题,但是逻辑稍微复杂一些且需要修改扩展某个地方时,需要改动很多地方。
      比如:再增加一个 QQSender,其需要实现Sender接口,并重写相关方法,然后需要在 SendMessage 类中 去修改相关代码,这里违反了开闭原则。若需要增加几个SendMessage 类,比如SendMessage2、SendMessage3时,同样需要改动很多代码。
    (5)UML图:

    二、简单工厂模式(SimpleFactory)

    1、什么是简单工厂模式:

      简单工厂模式属于创建型模式,不属于常见的23种常见模式。简单的讲 简单工厂模式是由一个工厂对象决定创建出哪一类产品的实例。

    2、使用:

    (1)简单工厂模式,定义了一个创建对象的类,然后由这个类来封装实例化对象的操作。
    (2)当需要大量创建某种类或者对象时,可以使用工厂模式。

    3、使用简单工厂模式实现:

    (1)定义一个发送工具的父类(接口),并将各种发送工具作为子类(实现类)。
    (2)定义一个工厂类,用于调用发送工具。
    (2)定义一个选择发送工具的类,用于调用工厂类。
    (3)代码实现:

    package creative.pattern.factory.simpleFactory;
    
    import java.util.Scanner;
    
    /**
     * 测试类
     *
     */
    public class SimpleFactoryDemo {
        public static void main(String[] args) {
            new SendMessage();
        }
    }
    
    /**
     * 定义一个发送工具的接口
     *
     */
    interface Sender {
        void sendMessage();
    }
    
    /**
     * 定义一个短信发送工具,实现接口,重写方法
     *
     */
    class ShortMessageSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送短信");
        }
    }
    
    /**
     * 定义一个微信发送工具,实现接口,重写方法
     *
     */
    class WeChatSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送微信");
        }
    }
    
    /**
     * 定义一个邮件发送工具,实现接口,重写方法
     *
     */
    class MailSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送邮件");
        }
    }
    
    /**
     * 使用简单工厂模式,管理需要生产的对象
     *
     */
    class SimpleFactory {
        /**
         * 用于获取需要发送信息工具
         */
        public static Sender createSender() {
            System.out.println("输入发送工具的类型(1-3):");
            System.out.println("1:短信");
            System.out.println("2:微信");
            System.out.println("3:邮件");
            Scanner scanner = new Scanner(System.in);
            String senderType = scanner.nextLine();
            if ("1".equals(senderType)) {
                return new ShortMessageSender();
            } else if ("2".equals(senderType)) {
                return new WeChatSender();
            } else if ("3".equals(senderType)) {
                return new MailSender();
            } else {
                return null;
            }
        }
    }
    
    /**
     * 定义一个选择发送工具的类
     */
    class SendMessage {
        public SendMessage() {
            do {
                Sender sender = SimpleFactory.createSender();// 选择发送信息的工具
                if (sender == null) {
                    System.out.println("欢迎下次使用");
                    break;
                } else {
                    sender.sendMessage();
                }
            } while (true);
        }
    }

    (4)代码分析:
      对于上述代码,SendMessage 只与工厂类SimpleFactory 相关联,此时需要扩展代码时,比如扩展QQSender,让其实现Sender并重写方法后,在SimpleFactory 中改变相关代码即可(违反开闭原则),不需要再改动SendMessage 的代码。
    (5)UML图:

    4、使用静态工厂模式实现(常用形式):

      将上例的SimpleFactory 中public Sender createSender()改为 public static Sender createSender()。
      调用时,直接使用SimpleFactory.createSender() 即可。

     public static Sender createSender() {
            System.out.println("输入发送工具的类型(1-3):");
            System.out.println("1:短信");
            System.out.println("2:微信");
            System.out.println("3:邮件");
            Scanner scanner = new Scanner(System.in);
            String senderType = scanner.nextLine();
            if ("1".equals(senderType)) {
                return new ShortMessageSender();
            } else if ("2".equals(senderType)) {
                return new WeChatSender();
            } else if ("3".equals(senderType)) {
                return new MailSender();
            } else {
                return null;
            }
        } 

    三、工厂方法模式

    1、什么是工厂方法模式:

      在工厂内部定义一个创建对象的抽象方法,由子类去确定要实例化的对象。简单的讲 工厂方法模式将对象实例化的操作推迟到子类去实现。可以看做抽象工厂模式的一个常见类型。

    2、使用工厂模式实现:

    (1)定义一个发送工具的父类(接口),并将各种发送工具作为子类(实现类)。
    (2)定义一个工厂方法接口,并通过工厂实现类去实例化对象。
    (3)定义一个选择发送工具的类,用于调用工厂实现类。
    (4)代码实现:

    package creative.pattern.factory.factoryMethod;
    
    import java.util.Scanner;
    
    /**
     * 工厂方法模式测试类
     *
     */
    public class factoryMethodDemo {
        public static void main(String[] args) {
            new SendMessage();
        }
    }
    
    /**
     * 定义一个发送工具的接口
     *
     */
    interface Sender {
        void sendMessage();
    }
    
    /**
     * 定义一个短信发送工具,实现接口,重写方法
     *
     */
    class ShortMessageSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送短信");
        }
    }
    
    /**
     * 定义一个微信发送工具,实现接口,重写方法
     *
     */
    class WeChatSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送微信");
        }
    }
    
    /**
     * 定义一个邮件发送工具,实现接口,重写方法
     *
     */
    class MailSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("发送邮件");
        }
    }
    
    /**
     * 使用工厂方法模式,让子类去创建对象
     *
     */
    interface FactoryMethod {
        Sender sendMessage();
    }
    
    /**
     * 短信工厂类,实现工厂方法类并重写相关方法
     *
     */
    class ShortMessageFactory implements FactoryMethod {
    
        @Override
        public Sender sendMessage() {
            return new ShortMessageSender();
        }
    }
    
    /**
     * 微信工厂类,实现工厂方法类并重写相关方法
     *
     */
    class WeChatFactory implements FactoryMethod {
    
        @Override
        public Sender sendMessage() {
            return new WeChatSender();
        }
    }
    
    /**
     * 邮件工厂类,实现工厂方法类并重写相关方法
     *
     */
    class MailFactory implements FactoryMethod {
    
        @Override
        public Sender sendMessage() {
            return new MailSender();
        }
    }
    
    /**
     * 定义一个选择发送工具的类
     */
    class SendMessage {
        public SendMessage() {
            do {
                System.out.println("输入发送工具的类型(1-3):");
                System.out.println("1:短信");
                System.out.println("2:微信");
                System.out.println("3:邮件");
                Scanner scanner = new Scanner(System.in);
                String senderType = scanner.nextLine();
                if ("1".equals(senderType)) {
                    FactoryMethod factoryMethod = new ShortMessageFactory();
                    Sender sender = factoryMethod.sendMessage();// 选择发送短信
                    sender.sendMessage();
                } else if ("2".equals(senderType)) {
                    FactoryMethod factoryMethod = new WeChatFactory();
                    Sender sender = factoryMethod.sendMessage();// 选择发送微信
                    sender.sendMessage();
                } else if ("3".equals(senderType)) {
                    FactoryMethod factoryMethod = new MailFactory();
                    Sender sender = factoryMethod.sendMessage();// 选择发送邮件
                    sender.sendMessage();
                } else {
                    System.out.println("欢迎下次使用");
                    break;
                }
            } while (true);
        }
    }

    (5)代码分析:
      SendMessage 类只与FactoryMethod 有关,当扩展新的功能时,比如QQSender,只需创建一个QQFactory,实现FactoryMethod 并重写其方法即可,调用时无需更改其他代码(必要的逻辑处理除外,符合开闭原则)。
    (6)UML图:

    四、抽象工厂模式

    1、什么是抽象工厂模式

      为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
    注:
      产品族:是指位于不同产品等级结构中功能相关联的产品组成的家族。
      产品等级结构:可以理解为一个接口或者一个抽象类。

    2、抽象工厂模式与工厂方法模式的区别

    (1)抽象工厂模式是工厂方法模式的升级版,其针对的是多个产品等级结构,即抽象工厂模式所提供的产品是衍生自不同的接口或抽象类。
    (2)工厂方法模式:针对一个产品等级结构,即工厂方法模式衍生自同一个接口或者抽象类。
    (3)如下图所示,ShortMessage 与 WeChat属于同一个产品等级,ShortMessage 与 ShortMessage2属于两个产品等级,为产品族。所以若存在两个产品等级及以上的情况,即为抽象工厂模式,若是同一个产品等级,则为工厂方法模式。
      下图为工厂方法模式:

    下图为抽象工厂模式:

    3、举例:

      在之前案例的基础上,增加一个功能,微信、短信均可以图片。
    (1)定义一个发送工具的父类(接口),并将各种发送工具作为子类(实现类)。
    (2)定义一个工厂方法接口,在方法中对产品族进行约束,并通过工厂实现类去实例化对象。
    (3)定义一个选择发送工具的类(测试类),用于调用工厂实现类。
    (4)代码实现:

    package creative.pattern.factory.absFactory;
    
    /**
     * 抽象工厂模式测试类
     *
     */
    public class AbsFactoryDemo {
        public static void main(String[] args) {
            AbsFactory absFactory = new WeChatFactory();
            absFactory.getSender().sendMessage();
            absFactory.getSender2().sendMessage();
            ((WeChatSender2) absFactory.getSender2()).sendPicture();
    
            AbsFactory absFactory2 = new ShortMessageFactory();
            absFactory2.getSender().sendMessage();
            absFactory2.getSender2().sendMessage();
            ((ShortMessageSender2) absFactory2.getSender2()).sendPicture();
        }
    }
    
    /**
     * 定义一个发送工具的接口
     *
     */
    interface Sender {
        void sendMessage();
    }
    
    /**
     * 定义一个微信发送工具,实现接口,重写方法
     *
     */
    class WeChatSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("使用微信,发送短信");
        }
    }
    
    /**
     * 定义一个微信发送工具,实现接口,重写方法,并新增自己的方法
     *
     */
    class WeChatSender2 implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("使用微信,发送短信");
        }
    
        public void sendPicture() {
            System.out.println("使用微信,发送图片");
        }
    }
    
    /**
     * 定义一个短信发送工具,实现接口,重写方法
     *
     */
    class ShortMessageSender implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("使用短信,发送短信");
        }
    }
    
    /**
     * 定义一个短信发送工具,实现接口,重写方法,并新增自己的方法
     *
     */
    class ShortMessageSender2 implements Sender {
    
        @Override
        public void sendMessage() {
            System.out.println("使用短信,发送短信");
        }
    
        public void sendPicture() {
            System.out.println("使用短信,发送图片");
        }
    }
    
    /**
     * 抽象工厂模式
     *
     */
    interface AbsFactory {
        Sender getSender();
    
        Sender getSender2();
    }
    
    /**
     * 工厂子类,实现抽象工厂类,重写相关方法,
     *
     */
    class WeChatFactory implements AbsFactory {
    
        @Override
        public Sender getSender() {
            return new WeChatSender();
        }
    
        @Override
        public Sender getSender2() {
            return new WeChatSender2();
        }
    }
    
    /**
     * 工厂子类,实现抽象工厂类,重写相关方法,
     *
     */
    class ShortMessageFactory implements AbsFactory {
    
        @Override
        public Sender getSender() {
            return new ShortMessageSender();
        }
    
        @Override
        public Sender getSender2() {
            return new ShortMessageSender2();
        }
    }

    (5)代码分析:
      对于产品族,定义在一个接口中,然后通过不同的子类去实现。扩展时,只需要实现接口并重写相关方法即可,满足开闭原则。

    (6)UML图:

    五、JDK中工厂模式举例(Calendar)

    1、部分源码

    public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {
        /**
         * Gets a calendar using the default time zone and locale. The
         * <code>Calendar</code> returned is based on the current time
         * in the default time zone with the default
         * {@link Locale.Category#FORMAT FORMAT} locale.
         *
         * @return a Calendar.
         */
        public static Calendar getInstance()
        {
            return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
        }
        
         private static Calendar createCalendar(TimeZone zone,
                                               Locale aLocale)
        {
            CalendarProvider provider =
                LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                     .getCalendarProvider();
            if (provider != null) {
                try {
                    return provider.getInstance(zone, aLocale);
                } catch (IllegalArgumentException iae) {
                    // fall back to the default instantiation
                }
            }
    
            Calendar cal = null;
    
            if (aLocale.hasExtensions()) {
                String caltype = aLocale.getUnicodeLocaleType("ca");
                if (caltype != null) {
                    switch (caltype) {
                    case "buddhist":
                    cal = new BuddhistCalendar(zone, aLocale);
                        break;
                    case "japanese":
                        cal = new JapaneseImperialCalendar(zone, aLocale);
                        break;
                    case "gregory":
                        cal = new GregorianCalendar(zone, aLocale);
                        break;
                    }
                }
            }
            if (cal == null) {
                // If no known calendar type is explicitly specified,
                // perform the traditional way to create a Calendar:
                // create a BuddhistCalendar for th_TH locale,
                // a JapaneseImperialCalendar for ja_JP_JP locale, or
                // a GregorianCalendar for any other locales.
                // NOTE: The language, country and variant strings are interned.
                if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                    cal = new BuddhistCalendar(zone, aLocale);
                } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                           && aLocale.getCountry() == "JP") {
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                } else {
                    cal = new GregorianCalendar(zone, aLocale);
                }
            }
            return cal;
        }
    }

    2、源码分析

      Calendar内部采用简单工厂模式进行对象的实例化。其根据不同的逻辑判断条件来选择实例化具体的对象。

    六、总结:

    1、工厂模式的意义:

      将实例化对象的代码提取出来,放到一个类(工厂类)里面进行维护,使其与主项目解耦,提高程序的维护性与扩展性。

    2、传统模式:

      直接在需要的地方实例化某对象。扩展代码时,需要在使用到的地方进行修改,违反了开闭原则。

    3、简单工厂模式:

      在需要用到的地方,调用工厂类即可,扩展代码时,修改工厂类即可,也违反了开闭原则。
    (1)简单工厂模式(普通方法):
      使用一个工厂类,在某方法中通过逻辑处理并实例化需要的对象。
    (2)简单工厂模式(静态方法):
      将简单工厂模式(普通方法)的普通方法改为静态方法,并通过”类名.方法名“来调用。
    (3)简单工厂模式(多方法):
      使用一个工厂类,并通过调用不同的方法去实例化不同的对象。

    4、工厂方法模式:

      使用一个工厂类接口与多个工厂实现类,在不同的工厂实现类中去实例化不同的对象。扩展代码时,定义一个工厂实现类,实现工厂类接口并重写相关方法即可,满足开闭原则。可以理解为抽象工厂模式的一般形式。

    5、抽象工厂模式:

      可以理解为工厂方法模式的升级版,其在一个接口中定义了一个产品族的处理(多个方法),子类实现该接口,并重写相关方法即可,扩展类似于工厂方法模式,满足开闭原则。

  • 相关阅读:
    bzoj 4260 Codechef REBXOR——trie树
    bzoj 2238 Mst——树链剖分
    bzoj 2836 魔法树——树链剖分
    CF 888E Maximum Subsequence——折半搜索
    bzoj 4289 PA2012 Tax——构图
    bzoj 4398 福慧双修——二进制分组
    bzoj1116 [POI2008]CLO——并查集找环
    bzoj4241 历史研究——分块
    bzoj4373 算术天才⑨与等差数列——线段树+set
    bzoj4034 [HAOI2015]树上操作——树链剖分
  • 原文地址:https://www.cnblogs.com/l-y-h/p/11305441.html
Copyright © 2020-2023  润新知