[设计模式] - 工厂模式(工厂方法与抽象工厂)

一 、 工厂模式的简介

1. 什么是工厂模式

工厂模式 (Factory)Pattern 隶属于创建型设计模式,是一种将对象创建过程在原有代码抽离,交于工厂类实现管理的设计模式。很多情况下,我们创建一个对象要维护大量的重复代码,针对这种情况我们就可以将对象的创建过程进行抽离,使用工厂对象达到复用的目的。

2. 工厂模式的分类

当然,工厂模式并不是只有一种,在《Design Patterns》一书中提到了两种不同的工厂模式:

  1. 工厂方法 (Factory Method)
  2. 抽象工厂(Abstract Factory)

其实对于上述两种工厂模式外,还有一种工厂方法模式的衍生模式:简单工厂 (Simple Method)。既然说是衍生模式,他和工厂方法在某种程度上还是存在些许异同。

二 、 通过业务场景引出工厂模式

1. 业务场景

最近小龙同学很是崇拜罗布斯,觉得既然罗布斯可以造锤子,那我也可以造一个榔头手机。于是小龙同学在朋友圈发出广告,接受定榔头手机。
榔头手机的亮点在于他搭载了一颗高性能的国产自研芯片晓龙666,并且拥有一颗10亿像素的摄像头,采用了4K,240HZ高清屏幕。并且榔头手机最大的亮点就是使用了一种自研系统洪荒。洪荒系统号称不会被任何病毒攻克,使用十年绝无卡顿!现在在朋友圈订购只要999!!!
如果此时有朋友觉得小龙同学创业不易,于是下了一单支持下小龙的国产原创,小龙同学接到订单并且开始定制手机,那么这个业务场景如果不使用工厂模式我们来模拟一下:

(1) 第一步:定制手机模板

public class HammerPhone {
    
    

	// 芯片
	private String chip;
	// 屏幕
	private String screen;
	// 摄像头
	private String camera;
	// 系统
	private String system;

	public void photograph() {
    
    
		System.out.println("榔头手机独家十亿摄像头为您服务!清晰到你可以看见您爱人的每一个雀斑!");
	}

	public HammerPhone() {
    
    
		System.out.println("欢迎使用榔头手机");
	}

	public HammerPhone(String chip, String screen, String camera, String system) {
    
    
		super();
		this.chip = chip;
		this.screen = screen;
		this.camera = camera;
		this.system = system;
		System.out.println("欢迎使用榔头手机");
	}

}

(2) 第二步:制造手机

public class PhoneOrder {
    
    

	public static void main(String[] args) {
    
    
		HammerPhone hammerPhone01 = new HammerPhone("晓龙666芯片", "4K,240HZ高清屏幕", "10亿像素摄像头", "洪荒系统");
		HammerPhone hammerPhone02 = new HammerPhone("晓龙666芯片", "4K,240HZ高清屏幕", "10亿像素摄像头", "洪荒系统");
		HammerPhone hammerPhone03 = new HammerPhone("晓龙666芯片", "4K,240HZ高清屏幕", "10亿像素摄像头", "洪荒系统");
		HammerPhone hammerPhone04 = new HammerPhone("晓龙666芯片", "4K,240HZ高清屏幕", "10亿像素摄像头", "洪荒系统");
		HammerPhone hammerPhone05 = new HammerPhone("晓龙666芯片", "4K,240HZ高清屏幕", "10亿像素摄像头", "洪荒系统");

	}
}

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

(3) 需求变更

榔头手机已经推出就受到了用户的喜爱,于是小龙同学趁热打铁,推出了榔头手机2.0版本,将芯片优化为晓龙888芯片,于此同时榔头手机1.0版本并不会停产。
如果我们在不使用工厂模式的情况下,会考虑修改下面的代码达到目的:
在这里插入图片描述
但是作为一个逼格很高的程序员,上面的代码明显存在三个问题:

  1. 语义不明: 一个HammerPhone对象代表了两个版本的榔头手机。
  2. 冗余太多: 重复的对象创建代码太多,可读性差。
  3. 修改麻烦: 如果调用方在多个类中,则此时需要修改多个类中关于对象创建相关的代码,违反了开闭原则。

三 、 工厂方法模式

1. 什么是工厂方法模式

工厂方法(Factory Method) 是指定义一个创建对象的接口,但是需要子类通过实现这个接口来决定具体实例化的是哪个类,工厂方法让对象的实例化延迟到了子类中进行。在工厂方法模式中,用户只需要关心所需产品对应的工厂,无需关心创建细节,而且加入新的产品时可拓展性好,不会违反开闭原则。
当然说了这么多,我们还是要具体的看下工厂方法模式该如何实现。

2. 工厂方法模式实现步骤

通过工厂方法模式生产一个榔头一代手机:

(1)创建手机工厂对象

public abstract class Phone {
    
    

	// 芯片
	protected String chip;
	// 屏幕
	protected String screen;
	// 摄像头
	protected String camera;
	// 系统
	protected String system;
	// 照相
	abstract void photograph();
	// 打电话
	abstract void call();
	// 装逼
	abstract void likeB();
}

(2)创建工厂方法接口

public interface PhoneFactory {
    
    

	Phone makePhone();
}

(3)实现工厂对象

public class HammerPhone_1st extends Phone {
    
    

	public HammerPhone_1st() {
    
    
		chip = "晓龙666芯片";
		screen = "4K";
		system = "240HZ高清屏幕";
		camera = "10亿像素摄像头";
	}

	@Override
	public void photograph() {
    
    
		System.out.println("十亿像素摄像头让一切雀斑无可逃避!");

	}

	@Override
	public void call() {
    
    
		System.out.println("性感荷官在线呼叫");

	}

	@Override
	public void likeB() {
    
    
		System.out.println("榔头一代手机,逼格就是高!");

	}

	@Override
	public String toString() {
    
    
		return "HammerPhone_1st [chip=" + chip + ", screen=" + screen + ", camera=" + camera + ", system=" + system
				+ "]";
	}

}

(4)实现工厂方法

public class HammerPhoneFactory implements PhoneFactory {
    
    

	@Override
	public Phone makePhone() {
    
    
		Phone hammerPhone_1st = new HammerPhone_1st();
		System.out.println(hammerPhone_1st.toString());
		return hammerPhone_1st;
	}

}

(5)创建订单类并测试结果

public class Order {
    
    
	
	public static void main(String[] args) {
    
    
		PhoneFactory hammerPhoneFactory = new HammerPhoneFactory();
		Phone makePhone = hammerPhoneFactory.makePhone();
	}
}

运行结果:
在这里插入图片描述
这里我们看到榔头一代手机已经可以正常量产了,那么如果我要新增一个手机类别榔头二代我应该如何做呢?很简单,我只需要增加对应的手机子类和手机工厂即可。

(6) 新增手机类别

实现一个榔头二代手机

public class HammerPhone_2nd extends Phone {
    
    

	public HammerPhone_2nd() {
    
    
		chip = "晓龙888芯片";
		screen = "4K,240HZ高清屏幕";
		system = "洪荒二代系统";
		camera = "15亿像素摄像头";
	}

	@Override
	public void photograph() {
    
    
		System.out.println("15亿像素摄像头让一切雀斑无可逃避!");

	}

	@Override
	public void call() {
    
    
		System.out.println("性感荷官在线呼叫");

	}

	@Override
	public void likeB() {
    
    
		System.out.println("榔头二代手机,逼格就是高!");

	}

	@Override
	public String toString() {
    
    
		return "HammerPhone_1st [chip=" + chip + ", screen=" + screen + ", camera=" + camera + ", system=" + system
				+ "]";
	}

}

(7) 新增手机工厂

public class HammerPhoneFactory_2nd implements PhoneFactory {
    
    

	@Override
	public Phone makePhone() {
    
    
		Phone hammerPhone_2nd = new HammerPhone_2nd();
		System.out.println(hammerPhone_2nd.toString());
		return hammerPhone_2nd;
	}

}

(8) 测试结果

public class Order {
    
    

	public static void main(String[] args) {
    
    
		PhoneFactory hammerPhoneFactory_2nd = new HammerPhoneFactory_2nd();
		hammerPhoneFactory_2nd.makePhone();
	}

}

在这里插入图片描述
这里看到榔头二代手机已经可以正常的生产了。

3. 工厂方法模式的设计原理

首先我们可以将上面的代码做一个简单的归类:
在这里插入图片描述
简单概括下工厂方法的创建步骤:

  1. 创建对象工厂(可以使用抽象类或者接口的形式)

这里解释下为什么我喜欢使用抽象类:Java语言是对现实世界的一种映射,那么抽象类和接口就是对现实世界的行为进行一种抽象描述,抽象类更偏向于去映射名词类也就是客观存在的实物,而接口更加适合来描述某种行为或规律。这个区分是为了让Java的语义更加明确!

  1. 创建对象实例(继承对象工厂)
  2. 创建工厂方法接口(使用Interface更佳)
  3. 创建工厂方法实例
  4. 调用工厂方法实例获得对象实例

4. 工厂方法的特点

优点:

  1. 用户只需要维护相对应的工厂,无需关注在业务中的创建细节
  2. 加入新的产品时符合开闭原则,不会对已有业务产生影响,可拓展性高。

缺点:

  1. 每次增加新的产品的时候都需要维护一个子类和一个子类工厂,增加了代码的复杂度。

四、简单工厂模式

1. 简单工厂模式的简介

简单工厂模式(simple factory) 实际上是针对工厂方法模式的一种简化,在我们谈到工厂方法的特点的时候,提到了每次新增一个产品就需要维护一个具体的产品实体和对应的实体工厂,这样的话在开发过程中创建了过多的类,增加了代码的复杂度。简单工厂模式就是用来解决过多的创建实体工厂的问题,他采用将所有实体工厂聚集到一个类或方法中,由业务选择具体需要创建哪种实体。

2. 代码实现

(1)新建实体工厂

public interface SimpleFactory {
    
    

	Phone makePhone(Integer type);
}

(2)新建工厂实现类

public class SimplePhoneFactory implements SimpleFactory {
    
    

	private final static Integer hammerPhone_1st = 1;
	private final static Integer hammerPhone_2nd = 2;

	@Override
	public Phone makePhone(Integer type) {
    
    
		Phone phone = null;
		if (type.equals(hammerPhone_1st)) {
    
    
			phone = new HammerPhone_1st();
		} else if (type.equals(hammerPhone_2nd)) {
    
    
			phone = new HammerPhone_2nd();
		}
		System.out.println(phone.toString());
		return phone;
	}

}

(3)测试

public class Order {
    
    

	public static void main(String[] args) {
    
    
		SimpleFactory simplePhoneFactory = new SimplePhoneFactory();
		Phone makePhone01 = simplePhoneFactory.makePhone(SimplePhoneFactory.hammerPhone_1st);
		Phone makePhone02 = simplePhoneFactory.makePhone(SimplePhoneFactory.hammerPhone_2nd);
	}

}

在这里插入图片描述

3. 简单工厂的设计思路

在这里插入图片描述
其实相较于工厂方法模式,简单工厂就是将工厂方法的创建结合到了一个简单工厂中,如果不明白的小伙伴可以对比下两种结构图就可以看出来区别。

4. 简单工厂的特点

优点:

  1. 通过一个工厂维护了所有的对象实例过程,复用性高
  2. 用户无需关心对象的创建过程,专注业务即可
  3. 可配合策略模式减少相关判断代码,提高程序的可读性。

缺点:

  1. 使用一个工厂类维护所有实例对象会导致如果这个工厂类发生异常则所有对象实例都将无法获取。这里还需要说一下,为了保证语义我并没有将简单工厂的工厂方法设置为静态方法,但是实际开发中为了减少对象创建频次,会考虑用static修饰工厂方法,这时候可能需要将接口实现方法更改为静态类继承方式。

五、抽象工厂模式

1. 什么是抽象工厂模式

抽象工厂(abstract factory): 是指提供一个创建一系列产品组的接口,通过子类实现接口来达到生产不同产品组的目的。业务调用方只需要关注使用的工厂子类就可以分别创建不同的产品组。
抽象工厂模式与工厂方法模式最大的不同就是抽象工厂模式关注的是一整个产品家族的管理,而工厂方法模式关注的是单一产品的创建。这里我们对上面的手机相关业务做一个需求拓展来讲述什么是抽象工厂模式。

小龙同学的榔头手机最近卖的很火,很受市场的欢迎。于是小龙同学准备布局下一步产业:智能汽车。此时榔头这个企业下已经有了两种类别的产品分别是手机和汽车。

2. 代码实现

如果使用工厂方法模式来实现,我们需要对应的创建智能汽车相关的实体和工厂方法,并不便于我们统一管理维护,这个时候我们就可以采用抽象工厂的方式来实现。

(1)创建汽车抽象类

public abstract class Car {
    
    

	// 车轱辘
	protected String tyre;

	abstract void run();

}

(2)创建具体汽车种类

public class HammersCar extends Car {
    
    

	@Override
	void run() {
    
    
		System.out.println("榔头汽车开起来就是贼拉快!");
	}

	public HammersCar() {
    
    
		System.out.println("榔头汽车已经创建");
	}

}

(3)创建抽象工厂接口

public interface AbstractFactory {
    
    

	Car makeCar();
	Phone makePhone();
}

(4)创建抽象工厂实现类

public class HammersAbstractFactory implements AbstractFactory {
    
    

	@Override
	public Car makeCar() {
    
    
		Car hammersCar = new HammersCar();
		return hammersCar;
	}

	@Override
	public Phone makePhone() {
    
    
		Phone hammerPhone_1st = new HammerPhone_1st();
		System.out.println(hammerPhone_1st.toString());
		return hammerPhone_1st;
	}

}

(5)测试效果

public class Orders {
    
    
	public static void main(String[] args) {
    
    
	 HammersAbstractFactory hammersAbstractFactory = new HammersAbstractFactory();
	 Car makeCar = hammersAbstractFactory.makeCar();
	 Phone makePhone = hammersAbstractFactory.makePhone();
	}
}

在这里插入图片描述

3. 抽象工厂的设计原理

老规矩,我们还是通过一个类的关系图进行描述
在这里插入图片描述
其实通过类图我们也可以看出抽象工厂和工厂方法主要区别就是在于抽象工厂是一个一对多的关系,他维护了一个产品的整个产品家族,可以通过一个抽象工厂获得你想要的相关产品的所有实例。

4. 抽象工厂的特点

优点:

  1. 产品的创建与业务隔离,业务调用方不需要关心对象的实例创建过程。
  2. 将一个产品组放到一个工厂进行维护可以更加便利的进行产品组相关功能拓展,例如数据记录,bi分析等。

缺点:

  1. 规定了所需要的产品组集合,当产品组需要拓展的时候,需要修改工厂代码,违背了开闭原则。
  2. 增加了代码的抽象性和复杂度。

六、 总结

总的来说,工厂方法和抽象工厂的创建过程相似,不过维护的对象粒度不同。工厂方法更适合单一对象的维护而抽象工厂跟适合一个产品系列的维护。
相同点:

  1. 将对象的创建过程抽离,业务方无需关心对象的创建过程。
  2. 都在某种程度上增加了系统的复杂度。

不同点:

  1. 抽象工厂更加关注产品组的维护而工厂方法更加关注单一产品的维护

好了,今天的内容到此结束,如果还有疑问的同学可以私信我或在评论区留言,我会在第一时间为你解答。觉得有收获的小伙伴请记得一键三连,关注博主不要走丢,拒当白嫖党~

猜你喜欢

转载自blog.csdn.net/xiaoai1994/article/details/112466597