工厂模式
工厂模式是在我们工作编码过程中最常用的设计模式之一,属于创建型模式,通过将创建、实例化对象的工作从应用中抽离出来,交给工厂统一处理达到松耦合的目的。
通常使用统一的接口来指向新创建的对象,比如:手机代工厂,我们制定一个手机的抽象类或接口,然后不同品牌的手机继承至这个类,子类可以定义自己的价格和一些属性,
工厂模式可以分为三类
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
1、简单工厂模式
简单工厂模式其实不是一个设计模式,它更像是一种编程习惯。
优点
- 一个调用者想创建一个对象,只要知道其名称就可以了。
- 屏蔽产品的具体实现,调用者只关心产品的接口。
- 方便扩展,如果需要增加其他类型的产品,只需要去更改工厂类的代码
简单工厂模式中,创建实例的方法是静态(static)方法,因为这样调用者不需要创建对象来实例化对象,但是这样做的缺点是不能通过继承来改变创建方法的行为
简单工厂模式主要有三个角色:
- Factory:工厂类,简单工厂的核心,负责实现创建所有产品的业务逻辑,创建所需要的对象,创建对象的方法通常为static,方便被外界直接调用。
- Product:产品抽象类,定义一些实现,这些实现可以被覆盖是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法 -ConcreteProduct:具体产品,工厂中创建目标,在具体类中我们可以重写父类的一些方法,更换具体产品的一些属性,它要实现抽象产品中声明的抽象方法
UML类图
}
下面我们来实现一个简单工厂
首先我们先定义一个工厂产品的基类
/**
* @Classname Pizza
* @Description TODO Pizza 产品抽象类,定义一些实现 ,这些实现可以被覆盖
* @Version 1.0.0
* @Date 2022/5/6 22:54
* @Created by 饭小范
*/
public abstract class Pizza {
public String name;
public String dough;
public String sauce;
/**
* 佐料
*/
public ArrayList<String> toppings = new ArrayList<>();
public void prepare(){
System.out.println("Preparing"+name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings:");
for (Object topping : toppings) {
System.out.println(" " + topping);
}
}
public void bake(){
System.out.println("bake for 25 minutes at 250");
}
public void cut(){
System.out.println("cutting the pizza into diagonal slices");
}
public void box(){
System.out.println("用圆形的盒子装");
}
public String getName(){
return name;
}
然后我们就可以定义一些具体的产品 比如芝士味的披萨
public class CheesePizza extends Pizza {
//TODO 在具体类中我们可以重写父类的一些方法,更换具体产品的一些属性,如Pizza的用料,切块的方法,价格等,这里就不去写具体代码了
}
定义简单工厂 负责实例化具体产品对象返回给客户
/**
* @Description TODO 简单工厂负责实例化对象并返回给工厂的客户
*/
public class SimplePizzaFactory {
public static Pizza createPizza(String type){
Pizza pizza = null;
//创建对象实例的业务逻辑
if (type.equals("cheese")){
pizza = new CheesePizza();
}else {
//TODO ....
}
return pizza;
}
}
定义工厂客户类
public abstract class PizzaStore {
//工厂的客户通过SimplePizzaFactory工厂来获取Pizza的实例
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type){
Pizza pizza;
pizza = factory.createPizza(type);
pizza.cut();
pizza.bake();
pizza.box();
return pizza;
}
}
简单工厂模式存在的问题
违反开闭原则,我们需要的产品(实例化的对象)可能有各种需求,比如我们需要增加新的产品,那就需要去修改工厂类的逻辑。
总结
简单工厂模式就是将实例对象的任务交给工厂类,工厂类通过客户所给的信息,来决定需要创建那个具体产品返回给客户,这样客户不需要自己去创建产品,只需要负责自己的业务。达到松耦合的目的。但是工厂类集中了所有产品实例的创建,将所有产品的创建逻辑集中在了一起,如果我们需要添加新的类型的产品,就需要去修改工厂类,这样对长期的维护非常不利,工厂方法模式就更好的解决了这个问题。
工厂方法模式
工厂方法模式,把工厂实例化的任务交由子类自己来负责,这样,子类就可以自定义一些自己想要的东西,由子类来决定该创建什么对象,来达到将对象创建的过程封装的目的
组成元素
- Creator(创建者): 抽象创建者类,它定义了一个抽象的工厂方法,让子类实现此方法制造产品
- ConcreteFactory(创建器对象): 工厂实现,覆盖创建者定义的工厂方法,返回具体的产品实例
- Product(抽象产品): 定义工厂发放所创建的对象的接口,也就是实际需要使用的对象的接口
- ConcreteProduct(具体产品): 工厂方法生产的具体产品对象
UML类图
classDiagram
Creator o-- ConcreteCreator
Product o-- ConcreteProduct
ConcreteProduct *-- ConcreteCreator
Creator : facttoryMethod()
ConcreteCreator : facttoryMethod()
代码实例
创建者:
* @Description TODO 抽象创建者类,定义了一个抽象的工厂方法 createPizza 来制造Pizza
public abstract class PizzaStore {
/**
* 创建Pizza订单,交由Pizza工厂方法来实例化不同风味Pizza
* @param type
* @return
*/
public Pizza orderPizza(String type){
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* 实例化pizza的工厂方法用来生产不同的pizza 将创建pizza功能交给子类自己来处理
* @param type
* @return
*/
protected abstract Pizza createPizza(String type);
}
具体创建者对象:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,将类的实例化交由子类来处理
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if ("cheese".equals(type)){
return new NYStyleCheesePizza();
}
return null;
}
}
抽象产品类
public abstract class Pizza {
public String name;
public String dough;
public String sauce;
/**
* 佐料
*/
public ArrayList<String> toppings = new ArrayList<>();
public void prepare(){
System.out.println("Preparing"+name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings:");
for (Object topping : toppings) {
System.out.println(" " + topping);
}
}
public void bake(){
System.out.println("bake for 25 minutes at 250");
}
public void cut(){
System.out.println("cutting the pizza into diagonal slices");
}
public void box(){
System.out.println("用圆形的盒子装");
}
public String getName(){
return name;
}
}
具体产品类
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
dough = "thin Crust Dough";
name = "纽约风味芝士Pizza";
sauce = "thin Crust Dough";
toppings.add("Crated reggiano cheese");
}
}
使用工厂方法创建产品
public class PizzaTestDrive {
public static void main(String[] args) {
NYPizzaStore nyPizzaStore = new NYPizzaStore();
Pizza cheese = nyPizzaStore.orderPizza("cheese");
}
}
总结
产品类和创建者类是两个平行的类层级,因为他们都有抽象类,每个抽象类的子类都有自己特定的实现 工厂方法使用面向对象多态的特性,定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法模式将类的实例化推迟到子类来实现,这样我们要添加新的产品时,就不需要去修改其他类的代码。 工厂方法模式的子类看起来很像简单工厂,简单工厂把全部的事情集中在一起处理,然而工厂方法是创建一个框架,让子类决定如何实现,让对象的创建更有弹性
优点
- 针对接口编程,而不是针对实现编程,扩展性更强,如果想要增加产品,只需要增加一个工厂方法类
- 调用者只需要关心产品的接口,而不需要注意具体实现
依赖倒置原则
要依赖抽象,不要依赖具体类,不能让高层的组件依赖底层组件,而且不管高层或者底层组件,两者都应该依赖于抽象 所谓高层组件,是由其他低层组件定义其行为的类,比如工厂方法中,创建者是高层组件,他的行为由创建器对象定义,抽象产品是低层组件,他的行为由具体产品定义
- 变量不可以持有具体类的引用
- 不要让类派生自具体类
- 不要覆盖基类中以实现的方法
抽象工厂模式
抽象工厂模式中引入了一个产品族的概念,所谓产品族,就是指位于不同产品等级结构中功能相关联的产品组成的家族
抽象工厂模式和工厂方法模式的区别在于,工厂放啊模式针对的是一个产品等级结构,而抽象工厂模式针对多个等级结构
定义
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
包含要素:
- AbstractFactory: 抽象工厂,用于声明生成抽象产品的方法,定义如何产生一个相关产品的家族
- ConcreteFacotry: 具体工厂,实现抽象工厂定义的方法负责一系列产品的创建
- AbstractProduct: 抽象产品,定义一类产品对象的接口
- ConCreteProduct: 具体产品,通常在具体工厂里,会选择具体的产品实现,来创建符合抽象工厂定义的方法返回的产品类型的对象。
代码实例
定义抽象工厂
/**
* @Description TODO 创建原料接口
*/
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Clams createClams();
Pepperoni createPepperoni();
String[] createVeggies();
}
定义Pizza原料的抽象工厂
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
return null;
}
@Override
public Sauce createSauce() {
return null;
}
@Override
public Cheese createCheese() {
return null;
}
@Override
public Clams createClams() {
return null;
}
@Override
public Pepperoni createPepperoni() {
return null;
}
@Override
public String[] createVeggies() {
return new String[0];
}
}
定义抽象产品Pizza原料
public interface Dough{
//TODO 这里只做简单演示,可以定义多个接口,一个接口代表一个产品族
}
定义具体产品 各种 Pizza原料
public class ThickCrustDough implements Dough{
//TODO 这里只做简单演示
}
然后我们使用客户来获取原料(产品)
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if ("cheese".equals(type)){
pizza = new CheesePizza(ingredientFactory);
pizza.setName("纽约风味芝士披萨");
}
return pizza;
}
}
总结
我们通过引入了新类型的工厂,也就是抽象工厂,来创建产品族,通过抽象共产所提供的接口,可以创建产品的家族,利用这个接口书写代码,我们的代码从实际工厂解耦,方便再不同的上下文中实现各式各样的工厂,制造出各种不同的产品,这样,我们可以替换不同的工厂来取得不同的产品
我们可以发现抽象工厂的每个方法看起来都像是工厂方法,,抽象工厂的任务是定义一个负责创建一组产品的接口,这个接口的每个方法都负责创建一个具体产品,同时我们利用实现抽象工厂的子类来提供这些具体的做法
抽象工厂模式优点
- 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
- 当我们需要增加新的产品族,不需要去修改原来的代码,符合开闭原则
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
- 可以把一群相关的产品集合起来形成产品族
抽象工厂模式和工厂方法的区别
- 抽象工厂使用接口组合,而工厂方法使用继承利用工厂方法创建对象,需要扩展一个类,并覆盖工厂方法,而抽象工厂提供了一个原来创建产品族的抽象类型,这个类型的子类定义了产品被产生的方法。
工厂模式总结
- 所有的工厂都是用来封装对象的创建的
- 简单工厂模式并不是一个真正的设计模式,但是是一个好的编码习惯,可以将我们的程序解耦
- 工厂方法使用继承把对象的创建交由子类,子类通过实现工厂方法来创建对象
- 抽象工厂使用对象组合(接口),对象的创建被实现在工厂接口所暴露出来的方法中
- 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合
- 抽象工厂创建相关的对象家族,不需要依赖于他们的具体实现
- 依赖倒置原则,要避免依赖具体类型,尽量依赖抽象
- 针对接口编程,而不是针对实现编程
参考
书籍:《Head First 设计模式》 博客: xie.infoq.cn/article/88c…