【设计模式】 - 创建者模式 - 工厂方法模式、抽象工程模式

前言

创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。

创建型模式分为:
单例模式
工厂方法模式
抽象工程模式
原型模式
建造者模式

工厂方法模式、 抽象工程模式

在这里插入图片描述
在这里插入图片描述

工厂模式是用来创建对象的一种最常用的设计模式,不暴露创建对象的具体逻辑,而是将将逻辑封装在一个函数中,那么这个函数就可以被视为一个工厂。
其就像工厂一样重复的产生类似的产品,工厂模式只需要我们传入正确的参数,就能生产类似的产品

举个例子:
编程中,在一个 A 类中通过 new 的方式实例化了类 B,那么 A 类和 B 类之间就存在关联(耦合)
后期因为需要修改了 B 类的代码和使用方式,比如构造函数中传入参数,那么 A 类也要跟着修改,一个类的依赖可能影响不大,但若有多个类依赖了 B 类,那么这个工作量将会相当的大,容易出现修改错误,也会产生很多的重复代码,这无疑是件非常痛苦的事;
这种情况下,就需要将创建实例的工作从调用方(A类)中分离,与调用方「解耦」,也就是使用工厂方法创建实例的工作封装起来(「减少代码重复」),由工厂管理对象的创建逻辑,调用方不需要知道具体的创建过程,只管使用,「而降低调用者因为创建逻辑导致的错误」;

1. 简单工厂模式(不属于GOF的23种经典设计模式)

简单工厂不是一种设计模式,反而比较像是一种编程习惯。

1.1 结构

抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
具体产品 :实现或者继承抽象产品的子类
具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

1.2 实现

在这里插入图片描述
在这里插入图片描述

缺点

增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

2. 工厂方法模式

2.1 结构

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

2.2 实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

优点、缺点

优点:用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
缺点:类的个数容易过多,增加复杂度
应用场景:客户只知道创建产品的工厂名,而不知道具体的产品名。

3. 抽象工厂模式

前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、传智播客只培养计算机软件专业的学生等。
这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。
本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。
在这里插入图片描述
在这里插入图片描述

3.1 概念

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

3.2 结构

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

3.3 实现

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

public interface DessertFactory {
    //生产咖啡的功能
    Coffee createCoffee();
    //生产甜品的功能
    Dessert createDessert();
}

具体工厂:

public class AmericanDessertFactory implements DessertFactory {
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
    public Dessert createDessert() {
        return new MatchaMousse();
    }
}
public class ItalyDessertFactory implements DessertFactory {
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
    public Dessert createDessert() {
        return new Trimisu();
    }
}

抽象产品:

public abstract class Dessert {
    public abstract void show();
}
public abstract class Coffee {
    public abstract String getName();
    //加糖
    public void addsugar() {
        System.out.println("加糖");
    }
    //加奶
    public void addMilk() {
        System.out.println("加奶");
    }
}

具体产品

public class AmericanCoffee extends Coffee {
    public String getName() {
        return "美式咖啡";
    }
}
public class LatteCoffee extends Coffee {
    public String getName() {
        return "拿铁咖啡";
    }
}
public class MatchaMousse extends Dessert {
    public void show() {
        System.out.println("抹茶慕斯");
    }
}
public class Trimisu extends Dessert {
    public void show() {
        System.out.println("提拉米苏");
    }
}

3.4 优点、缺点

如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。
优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

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

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

4.1 定义配置文件

为了演示方便,我们使用properties文件作为配置文件,名称为bean.properties

american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee

4.2 改进工厂类

静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需要执行一次。

public class CoffeeFactory {
	private static Map<String,Coffee> map = new HashMap();
	static {
		Properties p = new Properties();
		InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
		try {
			p.load(is);
			//遍历Properties集合对象
			Set<Object> keys = p.keySet();
			for (Object key : keys) {
				//根据键获取值(全类名)
				String className = p.getProperty((String) key);
				//获取字节码对象
				Class clazz = Class.forName(className);
				Coffee obj = (Coffee) clazz.newInstance();
				map.put((String)key,obj);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static Coffee createCoffee(String name) {
		return map.get(name);
	}
}

5. Collection.iterator方法

在这里插入图片描述
Collection接口是抽象工厂类,ArrayList是具体的工厂类;
Iterator接口是抽象商品类,ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品类的对象。
另:

  1. DateForamt类中的getInstance()方法使用的是工厂模式;
  2. Calendar类中的getInstance()方法使用的是工厂模式;

6. 工厂方法和抽象工厂区别

工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

工厂方法模式: 一个抽象产品类,可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式: 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类可以创建多个具体产品类的实例。

举个例子说明下:
用种蔬菜的例子来说明事实,最初的时候,由于规模小,只种植一种蔬菜,根菜类蔬菜,这个时候由于种植方式比较简单,采用简单工厂模式即可,主要目的是让工人轻松,下达工厂种植即可,但是随着种植厂的发展以及市场的需求,要增加一种蔬菜类型种植了,茎菜,由于茎菜与根菜种植方式不一致,就需要两个专门的种植工厂来进行管理,那么久采用工厂模式来管理,一个工厂负责一种作物的种植,这个时候产品可以理解为仍然在一个层次。但是随着科技的发展,我们逐步要种植转基因与非转基因食品了,在以前的蔬菜种类上又增加了一个层次,这个时候无法将其作为一个层次来解决,所以必须采用抽象工厂的方式来解决。我用UML图表示三种结构:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面的UML图很明显的就看出来了,抽象工厂可以创建多个产品类对象,如在种菜工厂中,有种根菜,种茎菜。工厂模式与抽象工厂模式以及简单工厂模式只有在具体应用的时候,分析具体的产品层级,然后选择相应的设计模式。
而在每一个层次,种菜工人所关心的对象也不一样,在简单工厂模式下,工人要想到种植萝卜还是白菜,在工厂模式下,工人想到是种植根菜还是茎菜,而在抽象工厂模式下,则关心种植基因菜还是非基因菜

来源:https://www.bilibili.com/video/BV1Np4y1z7BU?p=43&spm_id_from=pageDriver&vd_source=b901ef0e9ed712b24882863596eab0ca
工厂方法和抽象工厂区别:https://blog.csdn.net/wyxhd2008/article/details/5597975
https://www.jianshu.com/p/6060e0632071

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值