欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

创建模式--工厂模式、抽象工厂模式

程序员文章站 2022-07-16 23:46:34
一、小案例分析 1、功能需求: 实现一个发送信息的功能,要便于扩展与维护。(1)发送信息的工具有很多,比如短信、微信、邮件、QQ等。(2)选择某个工具进行信息发送。 2、小菜鸡去实现: (1)定义一个发送工具的父类(接口),并将各种发送工具作为子类(实现类)。(2)定义一个选择发送工具的类,用于调用 ......

一、小案例分析

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

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