Gof23设计模式之工厂方法模式和抽象工厂模式

news2024/11/28 10:38:36

在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。
如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。
以下以咖啡店来进行案例说明。

1.工厂方法模式

1.1.概念

定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

1.2.结构

工厂方法模式的主要角色:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

1.3.类图

在这里插入图片描述

1.4.实现

抽象咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 21:58
 *      抽象咖啡
 */
public abstract class Coffee {

    public abstract String getName();

    // 加糖
    public void addSugar() {
        System.out.println("加糖");
    }

    // 加奶
    public void addMilk() {
        System.out.println("加奶");
    }

}

拿铁咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:01
 *       拿铁咖啡
 */
public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

美式咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:01
 *      美式咖啡
 */
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}

咖啡工厂接口

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:31
 *      咖啡工厂接口
 */
public interface CoffeeFactory {

    Coffee createCoffee();

}

美式咖啡工厂类


/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:35
 *      美式咖啡工厂对象,专门用来生产美式咖啡
 */
public class AmericanCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

拿铁咖啡工厂类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:35
 *      拿铁咖啡工厂对象,专门用来生产拿铁咖啡
 */
public class LatteCoffeeFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

咖啡店类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:02
 *      咖啡店
 */
public class CoffeeStore {

    private CoffeeFactory coffeeFactory;


    public void setCoffeeFactory(CoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee() {
        Coffee coffee = coffeeFactory.createCoffee();

        coffee.addMilk();
        coffee.addSugar();
        return coffee;
    }

}

测试类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:39
 */
public class Client {

    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();

        CoffeeFactory coffeeFactory = new AmericanCoffeeFactory();
        coffeeStore.setCoffeeFactory(coffeeFactory);

        Coffee coffee = coffeeStore.orderCoffee();

        System.out.println(coffee.getName());
    }
}

在这里插入图片描述
也就是每一产品都有一个实现工厂接口的工厂实现类,我们在调用时,先将具体的工厂实现类传给咖啡店类,然后在进行点单和选择。

1.4.优缺点

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;

缺点:

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

2.抽象工厂模式

2.1.概念

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

2.2.结构

抽象工厂模式的主要角色如下:

  • 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
  • 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  • 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  • 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

2.3.类图

现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。
在这里插入图片描述

2.4.实现

抽象咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 21:58
 *      抽象咖啡
 */
public abstract class Coffee {

    public abstract String getName();

    // 加糖
    public void addSugar() {
        System.out.println("加糖");
    }

    // 加奶
    public void addMilk() {
        System.out.println("加奶");
    }

}

抽象甜品类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:51
 *      甜品抽象类
 */
public abstract class Dessert {

    public abstract void show();
}

甜品工厂接口类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:53
 *      甜品工厂
 */
public interface DessertFactory {

    // 生产咖啡的功能
    Coffee createCoffee();

    //
    Dessert createDessert();
}

美式咖啡类


/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:01
 *      美式咖啡
 */
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}

抹茶慕斯类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:53
 *      抹茶慕斯类
 */
public class MatchaMouse extends Dessert {
    @Override
    public void show() {
        System.out.println("抹茶慕斯");
    }
}

拿铁咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:01
 *       拿铁咖啡
 */
public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

提拉米苏类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:52
 *      提拉米苏类
 */
public class Trimisu extends Dessert {
    @Override
    public void show() {
        System.out.println("提拉米苏");
    }
}

意大利风味甜品工厂

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:56
 *         意大利风味甜品工厂
 *              生产拿铁咖啡和提拉米苏
 */
public class ItalyDessertFactory implements DessertFactory {
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new Trimisu();
    }
}

美式风味的甜品工厂

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:54
 *      美式风味的甜品工厂
 *              生产美式咖啡和抹茶慕斯
 */
public class AmericanDessertFactory implements DessertFactory {
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchaMouse();
    }
}

测试类


/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:57
 */
public class Client {

    public static void main(String[] args) {
        // 创建意大利风味甜品工厂
//        ItalyDessertFactory factory = new ItalyDessertFactory();

        AmericanDessertFactory factory = new AmericanDessertFactory();

        Coffee coffee = factory.createCoffee();
        Dessert dessert = factory.createDessert();

        System.out.println(coffee.getName());
        dessert.show();

    }
}

在这里插入图片描述

2.5.优缺点

优点:

当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

2.6.使用场景

  • 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。

  • 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。

  • 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

如:输入法换皮肤,一整套一起换。生成不同操作系统的程序。

3.模式扩展

3.1.简单工厂+配置文件解除耦合

可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。

3.2.实现

在src/resources目录下创建一个bean.properties

american=com.xfcy.factory.config_factory.AmericanCoffee
latte=com.xfcy.factory.config_factory.LatteCoffee

抽象咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 21:58
 *      抽象咖啡
 */
public abstract class Coffee {

    public abstract String getName();

    // 加糖
    public void addSugar() {
        System.out.println("加糖");
    }

    // 加奶
    public void addMilk() {
        System.out.println("加奶");
    }

}

美式咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:01
 *      美式咖啡
 */
public class AmericanCoffee extends Coffee {
    @Override
    public String getName() {
        return "美式咖啡";
    }
}

拿铁咖啡类

/**
 * @author 晓风残月Lx
 * @date 2023/6/18 22:01
 *       拿铁咖啡
 */
public class LatteCoffee extends Coffee {
    @Override
    public String getName() {
        return "拿铁咖啡";
    }
}

咖啡工程类


import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 8:31
 *      咖啡工厂接口  简单工厂/静态工厂+配置文件
 */
public class CoffeeFactory {

    // 加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储
    // 1.定义容器对象存储咖啡对象
    private static HashMap<String, Coffee> map = new HashMap<String, Coffee>();

    // 2.加载配置文件,只需要加载一次
    static {
        // 2.1 创建properties对象
        Properties properties = new Properties();
        // 2.2 调用对象中load方法进行配置文件的加载
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(is);
            // 从properties集合中获取全类名并创建对象
            Set<Object> keys = properties.keySet();
            for (Object key : keys) {
                String className = properties.getProperty((String) key);
                // 通过反射创建对象
                Class<?> clazz = Class.forName(className);
                Coffee coffee = (Coffee) clazz.newInstance();
                // 将名称和对象存储到容器中
                map.put((String) key, coffee);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 根据名称获取对象
    public static Coffee createCoffee(String name) {
        return map.get(name);
    }

}

测试类

/**
 * @author 晓风残月Lx
 * @date 2023/6/19 9:19
 *      简单工厂/静态工厂 + 配置文件
 */
public class Client {
    public static void main(String[] args) {
        Coffee coffee = CoffeeFactory.createCoffee("american");
        System.out.println(coffee.getName());

        System.out.println("===========");

        Coffee coffee1 = CoffeeFactory.createCoffee("latte");
        System.out.println(coffee1.getName());
    }
}

在这里插入图片描述
静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需要执行一次。
当然这种方法在Spring中应用极为广泛。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/664647.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习之K-Means(k均值)算法

1 K-Means介绍 K-Means算法又称K均值算法&#xff0c;属于聚类&#xff08;clustering&#xff09;算法的一种&#xff0c;是应用最广泛的聚类算法之一。所谓聚类&#xff0c;即根据相似性原则&#xff0c;将具有较高相似度的数据对象划分至同一类簇&#xff0c;将具有较高相异…

NDK使用LLVM编译Boost库给Android使用

1.下载boost库 ​ wget https://boostorg.jfrog.io/artifactory/main/release/1.71.0/source/boost_1_71_0.tar.gz​ 选择1.71.0版本 NDK版本19 ,ANDROID版本 24 进入然后后的目录 (不指定平台 默认为当前系统平台) ./bootstrap.sh --prefix=./android_build --libdir=.…

Java---阶段项目----五子棋

Java---阶段项目----五子棋 需求说明技术实现棋盘制作完整代码 需求说明 五子棋棋盘为一个1010的方格&#xff0c;五子棋玩家共为两个(A,B)&#xff0c;A在棋盘上落子后&#xff0c;B再落子&#xff0c;依次往复&#xff0c;直到一方胜利或者棋盘空间用完为止&#xff0c;判断…

StarCCM+ 命令行运行(Windows)

添加环境变量 找到启动程序的位置。找到当初安装starccm的文件夹&#xff0c;一般就是 Siemens 文件夹&#xff0c;进入会看到各版本的安装文件夹&#xff08;如果你没有安装多个版本则只有一个&#xff09;&#xff0c;然后参考下面我的路径找到相应的文件夹。在bin文件夹内可…

【openGauss简单使用---快速入门】

【openGauss简单使用---快速入门】 &#x1f53b; 一、openGauss使用&#x1f530; 1.1 连接openGauss&#x1f530; 1.2 创建数据库用户和授权&#x1f530; 1.3 创建数据库&#x1f530; 1.4 创建SCHEMA&#x1f530; 1.5 创建表 &#x1f53b; 二、总结—温故知新 &#x1f…

信息系统之网络安全方案 — “3保1评”

信息系统之网络安全方案 — “3保1评” 序&#xff1a;什么是“3评1保”&#xff1f;一、网络安全等级保护1.1 概念1.2等保发展1.3法律要求1.4分级及工作流程 二、涉密信息系统分级保护2.1概念2.2法律要求2.3分级及工作流程 三、关键信息基础设施保护3.1概念3.2关保的发展3.3法…

建立和使用Python自定义模块:打包+pip安装

文章目录 &#xff08;零&#xff09;拷目录-无法卸载&#xff08;一&#xff09;打包结构&#xff08;1.1&#xff09;__init__.py&#xff08;1.2&#xff09;setup.py &#xff08;二&#xff09;开始打包&#xff08;2.1&#xff09;命令出错&#xff1f; &#xff08;三&a…

构建高可用、高并发和高性能的微服务系统(Spring Cloud实现)

目前Java都在流行一个说词&#xff1a;高并发。 反正不管是不是&#xff0c;反正就是高并发。 谈高并发&#xff0c;我们需要知道几个名词&#xff1a; -响应时间(Response Time&#xff0c;RT)-吞吐量(Throughput)-每秒查询率QPS(Query Per Second)-每秒事务处理量TPS(Transa…

SuiteQlet Bundle

Content​​​​​​​ 1. Foreword 2. Overview 2.1 Glossary 2.2 Features 2.3 Design Description 3. Install 4. Setup 5. Instruction 5.1 Query 5.2 Chart 5.3 Publish Dashboard 6. Note 7. Video Link 1. Foreword SuiteQL is a powerful tool for data q…

python 第八章 集合set {}

系列文章目录 第一章 初识python 第二章 变量 第三章 基础语句 第四章 字符串str 第五章 列表list [] 第六章 元组tuple ( ) 第七章 字典dict {} 文章目录 8.1 创建集合8.2集合常见操作方法增加数据删除数据查找数据 8.1 创建集合 创建集合使用 { } 或 set()&#xff0c;但是如…

【MySQL】增删查改基础

目录 一、Create(创建) 1、insert(插入) 1.1单行数据插入 1.2多行数据插入 1.3插入或者替换更新 2、replace(替换) 二、Retrieve(读取) 1、select 1.1全列查询 1.2指定列查询利用selsct计算表达式 1.3筛选结果去重 2、where 2.1运算符 2.2找到英语小于60分的同学…

RFID课程要点总结_4 Tag Identification Protocol

4. Tag Identification Protocol Checksum procedure: parity checks, LRC, CRC 奇偶校验不多说&#xff0c;查1的个数&#xff0c;poor error recognition。电路通过所有位异或是偶校验&#xff0c;结果为1说明有错误&#xff1b;再取反是奇校验。 LRC longitudinal redund…

02 React组件、React组件实例的三大核心属性

总结 一、React组件 1.1 函数组件 定义 要求组件名称的首字母需要大写 function 组件名称(){ // 返回一段jsx的视图结构 return <div></div> }调用 <组件名称></组件名称> 或 <组件名称 /> 组件名首字母必须大写. 因为react以此来区分组件元…

一、动画 - 过渡效果transition

内容目录&#xff1a; 过渡动画&#xff1b;过渡动画的属性&#xff1b; 一、过渡动画 过渡&#xff08;transition&#xff09;作用&#xff1a;- 通过过渡可以指定一个属性发生变化时的切换方式- 通过过渡可以创建一些非常好的效果&#xff0c;提升用户的体验现在我们通过一…

nginx的操作手册和nginx的升级

总结 目录 一首先关闭防火墙和下载nginx包 1.安装依赖包 2.创建运行用户与组 3.进入nginx的目录下进行编译安装 4. 编译和编译安装 5.让系统识别nginx的操作命令 ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/ 6.添加nginx系统服务把nginx服务加入到…

Idea+maven+springboot项目搭建系列--3 整合阿里云Canal完成Mysql数据的监听

前言&#xff1a;在搭建canal 服务之后&#xff0c;项目中就可以连接canal &#xff0c;完成对感兴趣的数据库及其表中数据的变动完成监听&#xff0c;本文依赖于你已经完成了对canal 服务的搭建工作&#xff1b; 1 Cannal 特点&#xff1a; Canal是阿里巴巴开源的一款基于My…

Spring Boot实战:拦截器和监听器的应用指南

当使用Spring Boot时&#xff0c;我们可以通过拦截器&#xff08;Interceptor&#xff09;和监听器&#xff08;Listener&#xff09;来实现对请求和响应的处理。拦截器和监听器提供了一种可插拔的机制&#xff0c;用于在请求处理过程中进行自定义操作&#xff0c;例如记录日志…

使用自动化测试获取手机短信验证码

目前在职测试开发,,写一些脚本,个人认为这职业不科学不应该有的职业,测试就是测试,开发就是开发,运维还是老鸟,这行业总能折腾些莫名其妙的东西出来,刚做这行时学的第一门语言是bash shell, 去新去单位上班直接写了个一键搭建测试环境的测试脚本,本来不想干测试了,好好做微信小…

Linux 学习记录36(C高级篇)

Linux 学习记录36(C高级篇) 本文目录 Linux 学习记录36(C高级篇)一、文件相关指令1. chmod 修改文件用户权限(1. 权限字母表示法(2. 权限8进制表示法 2. 修改文件所属组(1. chgrp(2. chown 能够同时修改多个(3. 创建链接文件>1 ln创建硬链接文件>2 ln -s 创建软链接文件 …

7DGroup性能实施项目日记1

壬寅年 己酉月 丁丑日 2022年9月21日 晴 经过上周的7DGroup学员群内部沟通&#xff0c;我们决定启动一个性能实施项目。 在这个实施项目中&#xff0c;把RESAR性能工程的每个环节都落地一遍&#xff0c;让所有参与培训的学员都可以参与。 在这个项目实施过程中&#xff0c;我打…