【Java设计模式】三、

文章目录

  • 0、案例:咖啡屋
  • 1、简单工厂模式 + 静态工厂(不属于23种之列)
  • 2、工厂方法模式
  • 3、抽象工厂模式
  • 4、建造者模式
  • 5、原型设计模式

0、案例:咖啡屋

模拟咖啡店点餐。咖啡有多种,抽象类,子类为各种咖啡。咖啡店类聚合咖啡类。类图如下:

在这里插入图片描述

定义咖啡抽象类:

public abstract class Coffee {

	//获取咖啡种类名称
	public abstract String getName();

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

各种咖啡:

public class AmericanCoffee extends Coffee{

	@Override
	public String getName(){
		return "美式";
	}
}
public class LatteCoffee extends Coffee{

	@Override
	public String getName(){
		return "拿铁";
	}
}

咖啡屋类,聚合咖啡抽象类:

public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        } else {
        	throw new RuntimeException("店里没这种咖啡");
        }
        return coffee;
    }
}

以上代码的缺陷是咖啡类和 + 咖啡屋内耦合太高。下面用工厂模式解耦合。

1、简单工厂模式 + 静态工厂(不属于23种之列)

即由一个工厂决定创建哪一种产品类型的实例。 包括:

  • 抽象产品(抽象类)
  • 具体产品(子类)
  • 具体工厂(创建产品并提供方法给调用者)

改进上面的咖啡案例,引入工厂类,让咖啡屋不再自己创建咖啡对象,而是直接从工厂获取,类图:

在这里插入图片描述

/**
 * 咖啡工厂类
 */
public class SimpleCoffeeFactory {

    public Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffee;
    }
}
//新的咖啡屋类
public class CoffeeStore {

    public Coffee orderCoffee(String type) {
        SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
        Coffee coffee =  factory.createCoffee(type);
        //加配料
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

到这儿,有个疑惑,咖啡抽象类或子类变时,SimpleCoffeeFacroty类不还得变?这和直接咖啡屋类有啥区别?不都是改一个类?多此一举?其实不然,如果有一百家咖啡屋,而你没有工厂,那需求变更时你就得改一百次代码,而有了工厂,你只需改工厂一个类就行。本质还是这个工厂类带来了解耦。简单工厂可扩展为静态工厂(即把创建对象的方法改为静态的):

public class SimpleCoffeeFactory {
	
	//静态的
    public static Coffee createCoffee(String type) {
        Coffee coffee = null;
        if("americano".equals(type)) {
            coffee = new AmericanoCoffee();
        } else if("latte".equals(type)) {
            coffee = new LatteCoffee();
        }
        return coffe;
    }
}

但这种模式下,工厂类还是得修改,并不符合开闭原则。

2、工厂方法模式

  • 定义一个接口或者一个抽象的工厂类,让它的实现类(也是一个工厂)来决定创建哪一个实例对象。
  • 根据每个工厂不同的方法,来产生不同的所需要的对象

角色有:

  • 抽象工厂:只提供创建产品的接口给外界调用
  • 具体工厂:实现抽象工厂,完成具体产品的创建
  • 抽象产品:咖啡类
  • 具体产品:美式、拿铁

继续完善案例:

在这里插入图片描述

抽象工厂,只提供一个方法:

public interface CoffeeFactory {

	Coffee createCoffee();   //生产咖啡对象
	
}

具体的工厂类,实现抽象工厂:美式咖啡工厂、拿铁咖啡工厂

//美式咖啡工厂,专门用来生产美式咖啡
public class LatteCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new LatteCoffee();
    }
    
}

//拿铁咖啡工厂,专门用来生产拿铁咖啡
public class AmericanCoffeeFactory implements CoffeeFactory {

    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
    
}

注意现在的咖啡店类:1)、它依赖于抽象,聚合的是抽象工厂对象 2)、创建咖啡店对象,需要set传一个咖啡工厂对象

public class CoffeeStore {

    private CoffeeFactory factory;

	//通过构造方法来赋值
    public CoffeeStore(CoffeeFactory factory) {
        this.factory = factory;
    }
	
	//也可set
	public void setFactory(CoffeeFactory factory) {
		this.factory = factory;
	}
	

    public Coffee orderCoffee(String type) {
        Coffee coffee = factory.createCoffee();   //直接调抽象类的方法,到时是哪个子工厂,就能创建出哪种咖啡
        //加配料
        coffee.addMilk();
        coffee.addsugar();
        return coffee;
    }
}

测试类:

public class Client {

	public static void main(Stirng[] args) {
		
		//创建咖啡店对象
		CoffeeStore store = new CoffeeStore();
		//创建具体的咖啡工厂
		CoffeeFactory factory = new AmericanCoffeeFactory();
		store.setFactory(factory);
		//点咖啡
		Coffee coffee = store.orderCoffee();
		//获取咖啡名称
		System.out.println(coffee.getName());
	}
}

此时,再有新品种咖啡进来,只需新增代码NewCoffeeFactory去实现CoffeeFactory,以及新增Coffee的子类NewCoffee。测试类中自然就是:

//创建具体的咖啡工厂
CoffeeFactory factory = new NewCoffeeFactory();
store.setFactory(factory);
//....

符合开闭原则,并不会修改之前的代码。而缺点则是每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

3、抽象工厂模式

前面的工厂方法模式,生产的都是相同系列的对象,如Java课程、python课程。抽象工厂模式则是提供创建一系列相关或相互依赖对象的接口。比如生产汽车,将汽车分为车架、车门、底盘等各个零部件进行生产。

public interface CarFactory{

	//获取车门对象
	public CarDoor getCarDoor();
	//获取车架对象
	public CarFrame getCarFrame();
	//获取底盘对象
	public CarBasePlate getCarBasePlate();
	//制作汽车
	public void make();
}
//车门工厂
public abstract class CarDoorFactory{
	
	public abstract void make();
}
//底盘工厂
public abstract class CarBasePlateFactory{
	
	public abstract void make();
}
//车架工厂
public abstract class CarFrameFactory{
	
	public abstract void make();
}
//车门
public class CarDoor extends CarDoorFactory{
	@Override
	public abstract void make(){
		System.out.println("制作车门");
	}
}
//底盘
public class CarBasePlate extends CarBasePlateFactory{
	
	public abstract void make(){
		System.out.println("制作车底盘");
	}
}
//车架工厂
public class CarFrame extends CarFrameFactory{
	
	public abstract void make(){
		System.out.println("制作车架");
	}
}
public class Car implements CarFactory{

	private CarDoor carDoor = null;
	private CarFrame carFrame = null;
	private CarBasePlate carBasePlate = null;

	@Override
	public CarDoor getCarDoor(){
		carDoor = new CarDoor();
		return carDoor;
	}

	@Override
	public CarFrame getCarFrame(){
		carFrame = new new CarFrame();
		return carFrame;
	}
	
	@Override
	public CarBasePlate getCarBasePlate(){
		carBasePlate = new CarBasePlate();
		return carBasePlate;
	}

	@Override
	public void make(){
		carDoor.make();
		carFrame.make();
		carBasePlate.make();
		System.out.print("小汽车制作完成");
	}
}

测试:

public class Test{

	public static void mian(STring[] args){
		
		Car car  = new Car();
		car.getCarBasePlate();
		car.getCarFrame();
		car.getCarDoor();
		car.make();
	}
}

运行:
在这里插入图片描述

4、建造者模式

  • 将复杂的对象的创建 和 属性赋值所分离
  • 建造的过程和细节我们不需要知道,只需要通过构建者去进行操作
@Data
public class Car{

	private String basePlate;   //车底盘

	private String frame;	//车架

	private String door; 	//车门
}
public abstract class Builder{
	
	//车底盘
	public abstract void buildBasePlate(String basePlate);
	//车架
	public abstract void buildCarFrame(String carFrame);
	//车门
	public abstract void buildCarDoor(String carDoor);
	//制作车
	public abstract Car makeCar();
}

写实现类:

public class CarBuilder extends Builder{

	private Car car = new Car();

	@Overrid
	public abstract void buildBasePlate(String basePlate){
		car.setBasePlate(basePlate);
	}
	@Override
	public abstract void buildCarFrame(String carFrame){
		car.setFrame(frame);
	}
	@Override
	public abstract void buildCarDoor(String carDoor){
		car.setDoor(carDoor);
	}
	@Override
	public abstract Car makeCar(){
		return this.car;
	}
	
}

创建一个工程师:

public class Engineer{
	
	private CarBuilder carBuilder;

	//自动注入、构造方法、set方法都行,能完成赋值就行,这里写set
	public void setCarBuilder(CarBuilder carBuilder){
		this.carBuilder = carBuilder;
	}

	public Car mekeCar(String basePlate, String frame, String door){
		carBuilder.buildBasePlate(basePlate);
		carBuilder.buildCarFrame(frame);
		carBuilder.buildCarDoor(door);
		return carBuilder.makeCar();
	}
}

测试:

public class Test{

	public static void mian(STring[] args){
		Engineer engineer = new Engineer();
		CarBuilder carBuilder = new CarBuilder();
		engineer.setCarBuilder(carBuilder);
		Car car = engineer.makeCar("制作汽车底盘","制作汽车车架","制作汽车车门");
		System.out.println(car);
	}
}

运行:

在这里插入图片描述

5、原型设计模式

  • 用于创建重复的对象,能够保证创建对象的性能
  • 是创建对象的最佳方式
@Data
public class Pig{

	private String name;   //名字
	private String doSomething;  //喜欢做的事
	
}

现在要表示佩奇一家,正常创建流程如下:

public class Test{

	public static void mian(STring[] args){
		Pig peki = new Pig();
		peki.setName("佩琪");
		peki.setDoSomething("喜欢吃蛋糕");
		System.out.println(peki);

		Pig george = new Pig();
		george.setName("乔治");
		george.setDoSomething("喜欢睡觉");
		System.out.println(george);

		Pig pigDad = new Pig();
		pigDad.setName("猪爸爸");
		pigDad.setDoSomething("喜欢开车");
		System.out.println(pigDad);

		Pig pigMum = new Pig();
		pigMum.setName("猪妈妈");
		pigMum.setDoSomething("喜欢做饭");
		System.out.println(pigMum);
	}
}

运行:

在这里插入图片描述

采用原型设计模式后:实体类实现Cloneable接口

@Data
public class Pig implements Cloneable{

	public Pig() {
		System.out.println("小猪被初始化了...");
	}
	private String name;   //名字
	private String doSomething;  //喜欢做的事
	
	@Override
	protected Object clone() throws CloneNotSupportedException{
		return super.clone();
	}
}

再次创建佩奇一家:

public class Test{

	public static void mian(STring[] args){
		Pig peki = new Pig();    //先new一个
		peki.setName("佩琪");
		peki.setDoSomething("喜欢吃蛋糕");
		System.out.println(peki);

		Pig george = (Pig) peki.clone();    //后面就克隆
		george.setName("乔治");    //如果这里不赋值,那克隆出来的属性和克隆样本一样
		george.setDoSomething("喜欢睡觉");
		System.out.println(george);

		Pig pigDad = (Pig) peki.clone() ;
		pigDad.setName("猪爸爸");
		pigDad.setDoSomething("喜欢开车");
		System.out.println(pigDad);

		Pig pigMum = (Pig) peki.clone() ;
		pigMum.setName("猪妈妈");
		pigMum.setDoSomething("喜欢做饭");
		System.out.println(pigMum);
	}
}

运行:

在这里插入图片描述

发现构造方法只被调用了一次,且出来的也照样是不同的对象。因此,当对象属性很多,而又要创建大量这种对象时,就可以用原型设计模式。该模式产生的对象,虽然都是不同的对象,单如果不重新赋值,属性却是与克隆样本保持一致的,即使是一个新的对象。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

element ui富文本编辑器的使用(quill-editor)

引用组件 <el-form-item label"内容"><editor v-model"obj.activity_content" :min-height"192"/> </el-form-item> 组件封装 <template><div><el-upload:action"uploadUrl":before-upload"…

Node.js如何进行性能监控和分析

Node.js作为一款流行的后端开发技术&#xff0c;其性能监控和分析对于保证系统稳定性和性能优化至关重要。在本文中&#xff0c;我们将探讨Node.js如何进行性能监控和分析&#xff0c;以便开发者能够更好地了解系统运行状况并进行必要的优化。 为什么进行性能监控和分析&#…

力扣刷题:141.环形链表

题目&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中…

idea集成git详解教程(实用篇)

0.Git常用命令 Git常用命令-CSDN博客 1.下载git Git - Downloads 一路傻瓜式安装即可&#xff08;NEXT&#xff09; 2.软件测试 在Windows桌面空白处&#xff0c;点击鼠标右键&#xff0c;弹出右键菜单 Git软件安装后&#xff0c;会在右键菜单中增加两个菜单 Git GUI He…

GTID的使用原理

一.简介 GTID是MySQL 5.6的新特性&#xff0c;其全称是Global Transaction Identifier&#xff0c;可简化MySQL的主从切换以及Failover。GTID用于在binlog中唯一标识一个事务。当事务提交时&#xff0c;MySQL Server在写binlog的时候&#xff0c;会先写一个特殊的Binlog Event&…

机器学习提升秘籍:Scikit-learn学习网站全攻略!

介绍&#xff1a;是一个开源的Python机器学习库&#xff0c;它提供了一整套用于数据挖掘和数据分析的工具&#xff0c;包括各种分类、回归、聚类和降维算法以及模型评估、选择和数据预处理等功能。以下是关于Scikit-learn的一些详细介绍&#xff1a; 算法覆盖广泛&#xff1a;S…

基于SSM SpringBoot vue服装物流管理系统

基于SSM SpringBoot vue服装物流管理系统 系统功能 首页 图片轮播 人个中心 登录注册 后台管理: 登录注册 个人中心 货物信息管理 货物入库管理 订单信息管理 商品出库管理 快递追踪管理 用户管理 供应商信息管理 盘点信息管理 管理员管理 开发环境和技术 开发语言&#xf…

C++初阶:模版相关知识的进阶内容(非类型模板参数、类模板的特化、模板的分离编译)

结束了常用容器的介绍&#xff0c;今天继续模版内容的讲解&#xff1a; 文章目录 1.非类型模版参数2.模板的特化2.1模版特化引入和概念2.2函数模版特化2.3类模板特化2.3.1全特化2.3.1偏特化 3. 模板分离编译3.1分离编译概念3.2**模板的分离编译**分析原因 1.非类型模版参数 模板…

消息中间件之RocketMQ源码分析(二十二)

Broker主从同步流程 配置数据同步流程 配置数据包含4种类型:Topic配置、消费者位点、延迟位点、订阅关系配置。每种配置数据由一个继承自ConfigManager的类来管理&#xff0c;继承关系如图。Slave如何从Master同步这些配置呢?我们先来看一下初始化服务的步骤 第一步:Maste…

5个-最佳开源RPA框架

在最近两年中&#xff0c;RPA加上AI&#xff0c;即智能自动化流程&#xff0c;已经成为频繁讨论的话题&#xff0c;特别是在企业和机构的数字化转型过程中。自动化与智能化成为了提高效率的关键手段&#xff0c;而RPA便是迈向这一未来的起始步骤。 可以将RPA视为人体的躯干神经…

Docker 入门笔记

课程地址 容器技术概述 docker能做什么&#xff1a;将应用程序代码和依赖打包为一个镜像&#xff0c;作为交付介质&#xff0c;在各种环境中部署 相比于虚拟机&#xff0c;docker 只虚拟出一个隔离的程序运行环境&#xff0c;其需要则资源大大减少 容器内的程序就好像直接运…

pytorch 图像数据集管理

目录 1.数据集的管理说明 2.数据集Dataset类说明 3.图像分类常用的类 ImageFolder 1.数据集的管理说明 pytorch使用Dataset来管理训练和测试数据集&#xff0c;前文说过 torchvision.datasets.MNIST 这些 torchvision.datasets里面的数据集都是继承Dataset而来&#xff0c…

QT Mingw编译ffmpeg源码以及测试

文章目录 前言下载msys2ysamFFmpeg 搭建编译环境安装msys2安装QT Mingw编译器到msys环境中安装ysam测试 编译FFmpeg 前言 FFmpeg不像VLC有支持QT的库文件&#xff0c;它仅提供源码&#xff0c;需要使用者自行编译成对应的库&#xff0c;当使用QTFFmpeg实现播放视频以及视频流时…

Linux下快速创建大文件的4种方法总结

1、使用 dd 命令创建大文件 dd 命令用于复制和转换文件&#xff0c;它最常见的用途是创建实时 Linux USB。dd 命令是实际写入硬盘&#xff0c;文件产生的速度取决于硬盘的读写速度&#xff0c;根据文件的大小&#xff0c;该命令将需要一些时间才能完成。 假设我们要创建一个名…

Vuepress的使用

介绍 将markdown静态资源转换成html。 动态资源的转换还有很多&#xff0c;为什么要使用Vuepress&#xff1f; 目录分析 项目配置 详情 具体配置请看文档 插件配置 vuepress-theme-vdoing 主题插件 npm install vuepress-theme-vdoing -D先安装依赖配置主题 使用vuep…

外包干了6个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;2019年我通过校招踏入了重庆一家软件公司&#xff0c;开始了我的职业生涯。那时的我&#xff0c;满怀热血和憧憬&#xff0c;期待着在这个行业中闯出一片天地。然而&#xff0c;随着时间的推移&#xff0c;我发现自己逐渐陷入…

GEE入门篇|遥感专业术语(实践操作4):光谱分辨率(Spectral Resolution)

目录 光谱分辨率&#xff08;Spectral Resolution&#xff09; 1.MODIS 2.EO-1 光谱分辨率&#xff08;Spectral Resolution&#xff09; 光谱分辨率是指传感器进行测量的光谱带的数量和宽度。 您可以将光谱带的宽度视为每个波段的波长间隔&#xff0c;在多个波段测量辐射亮…

android开发与实战,那些年Android面试官常问的知识点

前言 在做android项目开发时&#xff0c;大家都知道如果程序出错了&#xff0c;会弹出来一个强制退出的弹 出框&#xff0c;这个本身没什么问题&#xff0c;但是这个UI实在是太丑了&#xff0c;别说用户接受不了&#xff0c;就连 我们自己本身可能都接受不了。虽然我们在发布程…

Vue:【亲测可用】父组件数组包对象,传给子组件对象,子组件修改属性(字段)后,父组件没有更新

场景&#xff1a;vue中父组件数组包对象&#xff0c;传给子组件对象&#xff0c;子组件修改属性&#xff08;字段&#xff09;后&#xff0c;父组件没有更新 代码&#xff1a; # 父组件 <div v-for"(object, name, index) in arr" :key"index"><…

【MySQL】数据管理——DML操作数据

目录 DML&#xff08;数据操作语言&#xff09;添加数据插入单行语法插入多行语法SQL示例将查询结果插入到新表中语法1&#xff1a;语法2&#xff1a; 修改数据语法示例关于SQL的运算符算术运算符比较运算符逻辑运算符 案例 删除数据DELETE命令语法 TRUNCATE TABLE 命令语法代码…