前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >04-02-设计模式 工厂模式

04-02-设计模式 工厂模式

作者头像
彼岸舞
发布2022-05-28 09:20:28
1610
发布2022-05-28 09:20:28
举报
文章被收录于专栏:java开发的那点事

简单工厂模式

需求

看一个披萨的项目:

  1. 披萨的种类有很多(GePizz, CePizz等)
  2. 披萨的制作步骤有 prepare, bake, cut, box
  3. 完成披萨店的订购功能

需求点: 要便于披萨种类的扩展, 要便于维护

类图

传统方式实现

代码语言:javascript
复制
package com.dance.design.designmodel.factory.simple;

public class CtPizz {
    public static void main(String[] args) {
        OrderPizza.ding("ce");
        OrderPizza.ding("ge");
    }
}

/**
 * 披萨类
 */
abstract class Pizza{
    /**
     * 名称
     */
    public String name;

    /**
     * 准备材料
     */
    public abstract void prepare();

    /**
     * 为了不每次都全部调用, 我创建一个构建披萨的方法
     */
    public void buildPizza(){
        prepare();
        bake();
        cut();
        box();
    }

    /**
     * 烘烤
     */
    public void bake(){
        System.out.println(name + "baking ...");
    }

    /**
     * 切块
     */
    public void cut(){
        System.out.println(name + "cutting ...");
    }

    /**
     * 打包
     */
    public void box(){
        System.out.println(name + "boxing ...");
    }

}

/**
 * 奶酪披萨
 */
class CePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备奶酪披萨的原材料");
        name = "奶酪披萨";
    }
}

/**
 * 希腊披萨
 */
class GePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备希腊披萨的原材料");
        name = "希腊披萨";
    }
}

/**
 * 订购类
 */
class OrderPizza{
    public OrderPizza(){}

    public static void ding(String orderType){
        Pizza pizza = null;
        if("ce".equals(orderType)){
            pizza = new CePizza();
        }else if("ge".equals(orderType)){
            pizza = new GePizza();
        }else{
            throw new RuntimeException("订单类型错误");
        }
        // 开始制作
        pizza.buildPizza();
    }
}

我这里没有写设么用户输入啥的,应为感觉意义不大, 我们主要看设计模式,而不是关注其他的边缘细节

传统实现方式分析

  1. 优点是比较好理解, 简单容易操作
  2. 缺点是违反了Ocp原则, 即对扩展开放,对修改关闭, 当我们给类增加新功能的时候尽量不修改代码,或者尽可能的少修改代码

需求改进

我们需要新增一种Pizza, 我们就需要创建新的Pizza种类 ,然后修改订单类的判断逻辑, 当然, 如果在别处还有创建的话, 需要在别处也修改, 这样的话改动量就会比较大

传统方式改进

我们新增巧克力披萨

代码语言:javascript
复制
/**
 * 巧克力披萨
 */
class QkPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备巧克力披萨的原材料");
        name = "巧克力披萨";
    }
}

修改OrderPizza逻辑, 这个创建的逻辑可能在很多地方都有,所以需要都修改, 我们这里只有一种效果看着还不是很麻烦

增加调用逻辑

到此扩展完毕, 应为设计的原因披萨类型扩展很容易, 调用扩展是必须的, 但是中间部分如果很多的话, 其实是可以统一管理的, 还有就是对于OrderPizza来说, 它其实是不应该知道Pizza的创建逻辑的, 而且如果后续扩展门店的话

会有orderPizza2, orderPizza3, 这样每个类都会和Pizza,Ce.., Ge.. Qk..全部发生关系, 如果一旦再增加Pizza的种类, 就会改动量非常大

简单工厂改进

基本介绍

  1. 简单工厂模式是属于创建型模式,是工厂模式的一种, 简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例, 简单工厂模式是工厂模式中最简单实用的
  2. 简单工厂模式: 定义一个创建对象的类, 由这个类来统一对实例进行创建
  3. 在软件开发中, 我们会用到大量的创建某种, 某类或者某批对象时, 就会使用到工厂模式

改进思路

把创建具体披萨的任务交给工厂, 这样我们创建的时候, 只需要调用工厂的方法即可, 其调用工厂的地方是不需要修改的

改进前类图

改进后类图

这样, 我让我的订单去面对工厂, 后续改造仅限于工厂, 种类, 和调用方, 但是订单是不需要修改的, 哪怕后续扩展订单门店, 也是直接面对工厂就可以了, 不必关注细节

代码

增加工厂类

代码语言:javascript
复制
class PizzaFactory{
    public static Pizza createPizza(String orderType){
        Pizza pizza = null;
        if("ce".equals(orderType)){
            pizza = new CePizza();
        }else if("qk".equals(orderType)){
            pizza = new QkPizza();
        }else if("ge".equals(orderType)){
            pizza = new GePizza();
        }else{
            throw new RuntimeException("订单类型错误");
        }
        return pizza;
    }
}

修改OrderPizza

代码语言:javascript
复制
/**
 * 订购类
 */
class OrderPizza{
    public OrderPizza(){}

    public static void ding(String orderType){
        // 开始制作
        PizzaFactory.createPizza(orderType).buildPizza();
    }
}

后续种类再则么多, 我的订单类都是不用动的

工厂方法模式

需求演化

我们的披萨店越做越大, 终于,在不同的地区开分店了,比如北京, 上海,广州等, 这个时候顾客就可以在不同的地区点北京的奶酪披萨, 上海的藤椒披萨等

思路

使用简单工厂模式, 我们可以创建BJPizzaFactory, SHPizzaFactory等, 从这案例来说是可以的, 但是考虑项目的规模,以为软件的可维护性和可扩展性并不是特别好, 所以我们用工厂方法模式

工厂方法模式介绍

工厂方法设计方案: 将披萨项目的实例化功能抽象成抽象方法, 在不同的口味点餐子类中做具体实现

工厂方法模式: 定义了一个创建对象的抽象方法, 由子类决定要实例化的类, 工厂方法模式将对象的实例化推迟到子类

类图

代码

代码语言:javascript
复制
package com.dance.design.designmodel.factory.simple.sp3;

import com.sun.org.apache.xpath.internal.operations.Or;

public class CtPizz {
    public static void main(String[] args) {
        new BJOrderPizza().ding("ce");
        new BJOrderPizza().ding("ge");
        new SHOrderPizza().ding("ce");
        new SHOrderPizza().ding("ge");
    }
}

/**
 * 披萨类
 */
abstract class Pizza{
    /**
     * 名称
     */
    public String name;

    /**
     * 准备材料
     */
    public abstract void prepare();

    /**
     * 为了不每次都全部调用, 我创建一个构建披萨的方法
     */
    public void buildPizza(){
        prepare();
        bake();
        cut();
        box();
    }

    /**
     * 烘烤
     */
    public void bake(){
        System.out.println(name + "baking ...");
    }

    /**
     * 切块
     */
    public void cut(){
        System.out.println(name + "cutting ...");
    }

    /**
     * 打包
     */
    public void box(){
        System.out.println(name + "boxing ...");
    }

}

/**
 * 北京奶酪披萨
 */
class BJCePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备北京奶酪披萨的原材料");
        name = "北京奶酪披萨";
    }
}
/**
 * 上海奶酪披萨
 */
class SHCePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备上海奶酪披萨的原材料");
        name = "上海奶酪披萨";
    }
}

/**
 * 希腊披萨
 */
class BJGePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备北京希腊披萨的原材料");
        name = "北京希腊披萨";
    }
}
/**
 * 希腊披萨
 */
class SHGePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备上海希腊披萨的原材料");
        name = "上海希腊披萨";
    }
}

/**
 * 订购类
 */
abstract class OrderPizza{
    public OrderPizza(){}

    public abstract void ding(String orderType);
}

/**
 * 北京店
 */
class BJOrderPizza extends OrderPizza {
    @Override
    public void ding(String orderType) {
        Pizza pizza = null;
        if("ce".equals(orderType)){
            pizza = new BJCePizza();
        }else if("ge".equals(orderType)){
            pizza = new BJCePizza();
        }else{
            throw new RuntimeException("订单类型错误");
        }
        // 开始制作
        pizza.buildPizza();
    }
}

/**
 * 上海店
 */
class SHOrderPizza extends OrderPizza {
    @Override
    public void ding(String orderType) {
        Pizza pizza = null;
        if("ce".equals(orderType)){
            pizza = new SHCePizza();
        }else if("ge".equals(orderType)){
            pizza = new SHCePizza();
        }else{
            throw new RuntimeException("订单类型错误");
        }
        // 开始制作
        pizza.buildPizza();
    }
}

这样我们调用只针对与门店, 上海调用上海的门店, 北京调用北京的门店, 然后由门店去面对Pizza和子类, 但是我感觉这样也不太好, 接下来使用抽象工厂改造~

抽象工厂模式

基本介绍

  1. 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  2. 抽象工厂模式可以将简单工厂和工厂方法模式进行整合
  3. 从设计层面看, 抽象工厂模式就是对简单工厂模式的改进(或者称之为进一步抽象)
  4. 将工厂抽象成两层, ABSFactory(抽象工厂), 和 具体实现的工厂子类, 程序员可以根据创建对象类型使用对应的工厂子类, 这样将单个的简单工厂类变成了工厂簇, 更利于代码的维护和扩展

类图

订单抽象类, 面向 抽象工厂, 抽象工厂面向披萨抽象类, 细节由实现类去维护

代码

代码语言:javascript
复制
package com.dance.design.designmodel.factory.simple.sp3;

public class CtPizz {
    public static void main(String[] args) {
        new BJOrderPizza(new BJPizzaFactory()).ding("ce");
        new BJOrderPizza(new BJPizzaFactory()).ding("ge");
        new SHOrderPizza(new SHPizzaFactory()).ding("ce");
        new SHOrderPizza(new SHPizzaFactory()).ding("ge");
    }
}
/**
 * 披萨类
 */
abstract class Pizza{
    public String name;
    public abstract void prepare();
    public void buildPizza(){
        prepare();
        bake();
        cut();
        box();
    }
    public void bake(){
        System.out.println(name + "baking ...");
    }
    public void cut(){
        System.out.println(name + "cutting ...");
    }
    public void box(){
        System.out.println(name + "boxing ...");
    }
}

/**
 * 北京奶酪披萨
 */
class BJCePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备北京奶酪披萨的原材料");
        name = "北京奶酪披萨";
    }
}
/**
 * 上海奶酪披萨
 */
class SHCePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备上海奶酪披萨的原材料");
        name = "上海奶酪披萨";
    }
}

/**
 * 北京希腊披萨
 */
class BJGePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备北京希腊披萨的原材料");
        name = "北京希腊披萨";
    }
}
/**
 * 上海希腊披萨
 */
class SHGePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("准备上海希腊披萨的原材料");
        name = "上海希腊披萨";
    }
}

/**
 * 订购类
 */
abstract class OrderPizza{

    /**
     * 面对抽象工厂
     */
    protected AbsPizzaFactory pizzaFactory;

    public OrderPizza(AbsPizzaFactory pizzaFactory){
        this.pizzaFactory = pizzaFactory;
    }

    public void ding(String orderType){
        // 默认逻辑
        pizzaFactory.createPizza(orderType).buildPizza();
    }
}

/**
 * 抽象工厂
 */
abstract class AbsPizzaFactory{
    protected abstract Pizza createPizza(String orderType);
}

/**
 * 北京披萨工厂
 */
class BJPizzaFactory extends AbsPizzaFactory{
    @Override
    protected Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if("ce".equals(orderType)){
            pizza = new BJCePizza();
        }else if("ge".equals(orderType)){
            pizza = new BJCePizza();
        }else{
            throw new RuntimeException("订单类型错误");
        }
        return pizza;
    }
}

/**
 * 上海披萨工厂
 */
class SHPizzaFactory extends AbsPizzaFactory{
    @Override
    protected Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if("ce".equals(orderType)){
            pizza = new SHCePizza();
        }else if("ge".equals(orderType)){
            pizza = new SHCePizza();
        }else{
            throw new RuntimeException("订单类型错误");
        }
        return pizza;
    }
}

/**
 * 北京店
 */
class BJOrderPizza extends OrderPizza {
    public BJOrderPizza(AbsPizzaFactory pizzaFactory) {
        super(pizzaFactory);
    }
}

/**
 * 上海店
 */
class SHOrderPizza extends OrderPizza {
    public SHOrderPizza(AbsPizzaFactory pizzaFactory) {
        super(pizzaFactory);
    }
}

其实抽象工厂我感觉还可以改进一下, 这样的话调用方就知道了具体的类,如果调用方多的话也不好维护, 可以再提供一个工厂创建者的单利, 通过传入地区也就是BJ, SH来获取一个抽象工厂(都已经学过单利模式了吧), 这个就自己去改造吧

源码剖析

JDK中的工厂模式

  1. JDK中的Calendar类中, 就使用了简单工厂模式

测试

代码语言:javascript
复制
public class CalendarTest {
    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance();
        System.out.println("年:" + calendar.get(Calendar.YEAR));
        // 月从0 开始 需要 +1
        System.out.println("月:" + (calendar.get(Calendar.MONTH)+ 1));
        System.out.println("日:" + calendar.get(Calendar.DAY_OF_MONTH));
        System.out.println("时:" + calendar.get(Calendar.HOUR_OF_DAY));
        System.out.println("分:" + calendar.get(Calendar.MINUTE));
        System.out.println("秒:" + calendar.get(Calendar.SECOND));
    }
}

如何,有感受到简单工厂的气息吗? 当然不只是简单工厂, 从第一行getInstance中也感受出来了吧, 这还是一个单利, 当然如果看源码的的话, 你就感受到很多其他的设计模式, 由此可以看出大佬是多么的**

源码

emm, 源码就不看了, 从使用上感受一下就可以了, 额, 要不还是看一下吧

代码语言:javascript
复制
public static Calendar getInstance(TimeZone zone,Locale aLocale){
  return createCalendar(zone, aLocale);
}

getInstance调用了createCalendar

代码语言:javascript
复制
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;
}

好了看完了吧

工厂模式小结

工厂模式的意义

将实例化细节的代码提取出来, 放到工厂类中统一管理和维护,达到和主业务线的依赖关系的解耦,从而提高了项目的扩展和维护性

三种模式

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

设计模式的依赖抽象原则

  1. 创建对象的依赖实例时, 不要直接new 类,而是吧这个new 类的动作放在一个工厂方法中,并返回, 有的书上说变量不要直接持有具体类的引用
  2. 不要让类继承具体类,而是继承抽象类 或者实现接口
  3. 不要覆盖基类中已经实现的方法
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简单工厂模式
    • 需求
      • 类图
        • 传统方式实现
          • 传统实现方式分析
            • 需求改进
              • 传统方式改进
                • 简单工厂改进
                  • 基本介绍
                  • 改进思路
                  • 改进前类图
                  • 改进后类图
                  • 代码
              • 工厂方法模式
                • 需求演化
                  • 思路
                    • 工厂方法模式介绍
                      • 类图
                        • 代码
                        • 抽象工厂模式
                          • 基本介绍
                            • 类图
                              • 代码
                              • 源码剖析
                                • JDK中的工厂模式
                                  • 测试
                                  • 源码
                              • 工厂模式小结
                                • 工厂模式的意义
                                  • 三种模式
                                    • 设计模式的依赖抽象原则
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档