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

一、小案例分析

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、抽象工厂模式:

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

(0)

相关推荐

  • 结构型设计模式 -- 适配器模式

    一.小案例分析 1.功能需求: 以电源适配器为例. 一个人去外国旅游,住在外国某宾馆,恰好手机没电(手机是双孔插头),但外国宾馆只有三孔插座,宾馆一般不可能为了旅客,将三孔插座换成双孔插座,此时适配器 ...

  • 每天学习一个设计模式(一):结构型之适配器模式

    一.基本概念 适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的的类的兼容性问题. 二.通俗解释 ADAPTER 适配器模式:在朋友聚会上碰到了一个美女Sar ...

  • Java设计模式之003--工厂模式

    工厂模式(Factory Pattern) 是 Java 中最常用的设计模式之一. 这种类型的设计模式属于创建型模式, 它提供了一种创建对象的最佳方式. 在工厂模式中, 我们在创建对象时不会对客户端暴 ...

  • Java设计模式之002--策略模式

    策略模式(Strategy Pattern) :定义了一系列的算法, 将每一种算法封装起来并可以相互替换使用, 策略模式让算法独立于使用它的客户应用而独立变化. OO设计原则: 1.面向接口编程(面向 ...

  • 设计模式之迭代器与组合模式(二)

    在上次的文章中,我们通过层层引导,已经知道了迭代器模式的由来.现在我们再好好总结下. 关于迭代器模式,你所需要知道的第一件事情,就是它依赖于一个名为迭代器的接口.这是一个可能的迭代器的接口: 现在,我 ...

  • 如何学习23种设计模式及其思想?

    感觉设计模式是看着简单 ,但是一用就不会,23种设计模式,学的人头大,相信大家都是这样的 设计模式在程序员的面试中会被考到,通常是介绍其原理并说出优缺点.或者对比几个比较相似的模式的异同点.在笔试中可 ...

  • 【设计模式】工厂方法模式(Factory Method)

    工厂方法模式(Factory Method) 工厂方法模式分为三种: 1.普通工厂模式 就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建.首先看下关系图:举例如下:(我们举一个发送邮件和短信 ...

  • 面向对象23种设计模式系列(一)- 创建型设计模式

    本章是面向对象23种设计模式系列开篇,首先我们来看下什么是设计模式? 面向对象23种设计模式: 1.面向对象语言开发过程中,遇到的种种场景和问题,提出了解决方案和思路,沉淀下来就变成了设计模式. 2. ...

  • 创建型设计模式总结

    Intro 前面几篇文章已经把创建型设计模式都介绍了,来做一个简单的总结. 创建型设计模式,就是用来创建对象的设计模式,根据要创建的对象的复杂度以及是否允许多实例以及是否需要容易扩展等多方面考虑去选择 ...

  • 创建型设计模式--单例模式

    一.单例模式 1.什么是单例模式 采取一定的方法,使程序中的某个类只存在一个实例对象,且该类对外提供一个获取该对象的方法(一般为静态方法). 2.单例模式分类 (1)饿汉式(2种写法,线程安全) 静态 ...

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

    前一章节,我们介绍了简单工厂模式以及工厂方法模式,但是这两种模式都存在一定的局限性,只能生产某一类型下的某一种产品,如果需求变更,同类型下出现了不同的产品,比如芝士披萨不仅有口味上的不同,同时存在外观 ...

  • 设计模式-创建型-工厂模式

    工厂设计模式: 顾名思义,该模式是用来生产对象的.在面向对象的设计模式中,万物皆对象,若使用new来创建对象,就会对该对象产生强耦合,假如我们需要更换该对象,那么使用该对象的对象都需要进行修改,这显然 ...

  • 【8/25】使用抽象工厂模式(Abstract Factory Pattern) 封装页面对象的创建过程

    这是<小游戏从0到1设计模式重构>系列内容第8篇,所有源码及资料在"程序员LIYI"公号回复"小游戏从0到1"获取. 看完了三姐妹中的大姐.二姐,最 ...

  • PHP设计模式之抽象工厂模式

    PHP设计模式之抽象工厂模式 工厂模式系列中的重头戏来了,没错,那正是传闻中的抽象工厂模式.初次听到这个名字的时候你有什么感觉?反正我是感觉这货应该是非常高大上的,毕竟包含着"抽象" ...

  • [PHP小课堂]PHP设计模式之抽象工厂模式

    [PHP小课堂]PHP设计模式之抽象工厂模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • 设计模式——把类作为参数的抽象工厂模式

    今天给大家介绍一个非常简单的设计模式,一学就会,非常好用. 这个模式叫做抽象工厂模式,大家可能对工厂模式比较熟悉,在工厂模式当中封装了实例的创建逻辑.主要的用途一般是将一些复杂的类的创建过程整合在一起 ...