定义
工厂模式
工厂模式定义了一个创建对象的接口,但由子类来确定要实例化哪个类。工厂方法让类把实例化推迟到子类
抽象工厂模式
抽象工厂模式提供一个接口来创建相关或依赖对象的家族,而不需要指定具体类
构成
工厂模式
- 产品(Product):
- 定义了产品的接口或抽象类,规范了产品需要实现的方法。
- 具体产品(Concrete Product):
- 实现了产品接口的类,定义了将被创建的具体产品。
- 创建者(Creator):
- 定义了一个用于创建产品的方法,可以是抽象的,也可以提供默认的产品实现。
- 具体创建者(Concrete Creator):
- 继承或实现创建者类,重写或实现创建产品的方法,用于创建一个或多个具体产品。
抽象工厂模式
- 抽象工厂(Abstract Factory):
- 定义了一个用于创建一系列相关或相互依赖对象的接口。
- 具体工厂(Concrete Factory):
- 实现抽象工厂接口的类,每个类创建一系列特定的产品。
- 抽象产品(Abstract Product):
- 定义了一系列相关产品的接口。
- 具体产品(Concrete Product):
- 实现抽象产品接口的类,定义了将被具体工厂创建的具体产品。
共同点和区别
共同点
1. 两种设计模式都属于创建型设计模式,目的是为了更好地创建对象。抽象工厂模式通过具体工厂类创建产品族,工厂模式通过具体创建者类创建单一产品。
2. 两者都隐藏了对象创建的细节,使得系统更加模块化,也降低了系统中各个类之间的耦合度,易于扩展和维护。
区别
-
作用不同,工厂模式用于创建单一产品或一类产品,适用于产品种类单一或产品无复杂关联关系的场景。而抽象工厂模式通常用于更复杂的产品族,可以创建多个系列的产品,适用于产品组合多样化,且产品之间有关联或依赖关系的场景。
-
扩展性有区别,对于工厂模式来说创建新的产品相对简单,对于抽象工厂模式如果需要新增产品的话则需要修改抽象工厂以及每个子类的接口。
优点
工厂模式
- 创建对象的灵活性:客户端可以通过工厂接口创建新对象,而无需知道具体的类名。
- 扩展性:添加新的产品类时,只需添加一个具体的产品类和相应的工厂类,不需要修改现有代码。
- 屏蔽产品的具体实现:客户端只依赖产品接口,不关心产品的具体实现,降低了客户端与产品实现之间的耦合。
抽象工厂模式
- 产品族的一致性:确保一个系列中的产品能够一起工作,提高了产品之间的兼容性。
- 系统的隔离性:客户端代码从具体的产品实现中解耦,只依赖于产品的接口。
- 易于交换产品系列:由于产品系列是在抽象工厂级别定义的,因此切换不同的产品系列变得容易。
缺点
工厂模式
- 类的数量可能增多:每增加一个新的产品,都需要增加一个具体产品类和一个具体工厂类,可能导致系统中类的数量急剧增加。
- 增加了系统的抽象性:虽然减少了客户端与具体产品之间的耦合,但增加了客户端与工厂之间的复杂性。
抽象工厂模式
- 难以支持新种类的产品:如果需要添加新的产品(不是产品族),则需要修改抽象工厂及其所有子类,违反了开闭原则。
- 复杂性增加:随着产品族的增加,相关的类和对象的数量也会增加,增加了系统的复杂度。
场景
工厂模式
- 日志记录器:系统可能需要支持多种日志记录方式,如文件日志、数据库日志或网络日志,工厂模式可以在运行时根据配置创建相应的日志记录器。
- 连接对象:在网络库中,根据不同的协议(如HTTP、FTP)创建不同类型的连接对象。
- 支付方式:在电子商务平台中,根据用户选择的支付方式(如信用卡、PayPal或Apple Pay)创建相应的支付对象。
抽象工厂模式
- 跨平台UI组件库:当需要为不同的操作系统提供一套UI组件时,如Windows、MacOS和Linux,抽象工厂模式允许客户端代码无需修改即可在这些平台上运行。
- 数据库访问层:当系统需要支持多种数据库时,如MySQL、Oracle和SQL Server,抽象工厂模式可以在不改变客户端代码的情况下切换不同的数据库。
案例
描述
一个披萨连锁店需要在纽约和芝加哥建立门店,由于不同地区的饮食风格在对披萨的品类和原料的使用均有差异,所以需要系统在不同地区能够高效的提供不同的原料以及推荐针对地区风味的披萨。
思路
对应披萨的不同种类可以对应为工厂模式中的产品,不同地区的门店视为对应的工厂,在这里可以使用工厂模式进行处理。而披萨中的原料可以理解为产品族,每个地区要提供不同的原料,每个披萨也会使用不同的原料,对应原料的供应可以使用抽象工厂进行处理。
类图
代码
Pizza父类和子类(产品)
public abstract class Pizza {
/**
* 名字
*/
String name;
/**
* 面团类型
*/
Dough dough;
/**
* 酱汁
*/
Sauce sauce;
abstract void prepare();
public String getName() {
return name;
}
void bake(){
System.out.println("350度烘焙24分钟。");
}
void cut(){
System.out.println("斜切成小块");
}
void box(){
System.out.println("放入普通包装");
}
}
public class NYStylePizza extends Pizza{
private PizzaIngredientFactory pizzaIngredientFactory;
public NYStylePizza(PizzaIngredientFactory pizzaIngredientFactory) {
this.pizzaIngredientFactory = pizzaIngredientFactory;
this.name = "纽约风味披萨";
}
@Override
void prepare() {
System.out.println("准备中..."+name);
dough = pizzaIngredientFactory.createDough();
System.out.println("加入"+dough.getName());
sauce= pizzaIngredientFactory.createSauce();
System.out.println("加入"+sauce.getName());
}
void bake(){
System.out.println("300度烘焙30分钟。");
}
}
public class ChicagoStylePizza extends Pizza{
private PizzaIngredientFactory pizzaIngredientFactory;
public ChicagoStylePizza( PizzaIngredientFactory pizzaIngredientFactory) {
this.pizzaIngredientFactory = pizzaIngredientFactory;
this.name = "芝加哥风味披萨";
}
@Override
void prepare() {
System.out.println("准备中..."+name);
dough = pizzaIngredientFactory.createDough();
System.out.println("加入"+dough.getName());
sauce= pizzaIngredientFactory.createSauce();
System.out.println("加入"+sauce.getName());
}
void box(){
System.out.println("芝加哥包装");
}
}
PizzaStore父类和子类(抽象创建者和具体创建者)
public abstract class PizzaStore {
public Pizza OrderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type);
}
public class ChicagoPizzaStore extends PizzaStore{
@Override
protected Pizza createPizza(String type) {
Pizza pizza =null;
ChicagePizzaIngredientFactory chicagePizzaIngredientFactory = new ChicagePizzaIngredientFactory();
if (type.equals("ChicagoStyle")){
pizza = new ChicagoStylePizza(chicagePizzaIngredientFactory);
}else if(type.equals("pepperoni")){
//其他口味的
}else if(type.equals("clam")){
}
return pizza;
}
}
public class NYPizzaStore extends PizzaStore{
@Override
protected Pizza createPizza(String type) {
NYPizzaIngredientFactory nyPizzaIngredientFactory = new NYPizzaIngredientFactory();
Pizza pizza =null;
if (type.equals("NYStyle")){
pizza = new NYStylePizza(nyPizzaIngredientFactory);
}else if(type.equals("pepperoni")){
//其他口味的
}else if(type.equals("clam")){
}
return pizza;
}
}
Dough接口和实现(抽象产品和具体产品)
public interface Dough {
String getName();
}
public class NYDough implements Dough{
@Override
public String getName() {
return "纽约面团";
}
}
public class ChicagoDough implements Dough{
@Override
public String getName() {
return "芝加哥面团";
}
}
Sauce接口和实现(抽象产品和具体产品)
public interface Sauce {
String getName();
}
public class NYSauce implements Sauce{
@Override
public String getName() {
return "纽约酱汁";
}
}
public class ChicagoSauce implements Sauce{
@Override
public String getName() {
return "芝加哥酱汁";
}
}
PizzaIngredientFactory接口和实现(抽象工厂和具体工厂)
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
}
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
return new NYDough();
}
@Override
public Sauce createSauce() {
return new NYSauce();
}
}
public class ChicagePizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
return new ChicagoDough();
}
@Override
public Sauce createSauce() {
return new ChicagoSauce();
}
}
测试和结果
public class Test {
public static void main(String[] args) {
NYPizzaStore nyPizzaStore = new NYPizzaStore();
ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();
Pizza pizza = nyPizzaStore.OrderPizza("NYStyle");
System.out.println("订单:"+pizza.getName()+"已完成");
pizza = chicagoPizzaStore.OrderPizza("ChicagoStyle");
System.out.println("订单:"+pizza.getName()+"已完成");
}
}
准备中...纽约风味披萨
加入纽约面团
加入纽约酱汁
300度烘焙30分钟。
斜切成小块
放入普通包装
订单:纽约风味披萨已完成
准备中...芝加哥风味披萨
加入芝加哥面团
加入芝加哥酱汁
350度烘焙24分钟。
斜切成小块
芝加哥包装
订单:芝加哥风味披萨已完成
理解
抽象工厂模式和工厂模式虽然有很多不同之处,但是并不影响这两个模式同时出现,抽象工厂的方法经常实现为工厂方法,在上面的案例中就表现出来了这点。抽象工厂的工作是定义一个接口,这个接口创建一组产品。这个接口的每个方法负责创建一个具体的产品,我们实现抽象工厂的子类,以提供这些实现,因此,在抽象工厂中,用工厂方法来实现生产方法是相当合理的方式。
在使用这两个设计模式需要首先要明确需求。如果需要处理一系列相关的产品并且希望它们能够一起工作,那么抽象工厂模式可能更适合。如果只需要创建一个产品或一类产品,那么工厂模式可能更合适。在添加新的产品或产品族时,尽量不要修改现有的代码,而是通过添加新的代码来扩展功能。