详解JAVA面向对象的设计模式 (三)、工厂模式
工厂模式 Factory
介绍
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
前言
工厂模式主要分为三种形式
-
简单工厂模式(不属于GoF的23种设计模式中)
-
工厂方法模式
-
抽象工厂模式
简单来说,本质上工厂模式是对创建对象过程的一种封装。通过封装,让客户端无需在意对象创建的过程和逻辑。看看下面的例子。
本文部分解释摘录自 http://c.biancheng.net/view/1351.html
简单工厂模式
比如我们去一家餐厅。我们想要点餐吃饭。
那么可以看作是客户得到了饮料,食物,餐具,然后一一使用。我们先以下单食物为例,写个例子。
不使用工厂模式时是这样的
public static void main(String[] args) {
// 普通创建对象
Hamburger hamburger = new Hamburger();
Steak steak = new Steak();
hamburger.eat();
steak.eat();
}
然而这样的创建对象,语义上不清晰且需要在客户端中初始化对象。
毕竟给我们制作食物的是餐厅,所以我们可以把餐厅理解成一种工厂。我们客户向餐厅点单就行了。
在例子中实现
首先,餐厅生产的食品Food,都可以食用,创建接口eat。
public interface Food {
void eat();
}
那么使用汉堡和牛扒就应该如下实现
public class Hamburger implements Food {
@Override
public void eat() {
System.out.println("吃汉堡,嘎嘎嘎");
}
}
public class Steak implements Food {
@Override
public void eat() {
System.out.println("吃牛扒,嘎嘎嘎");
}
}
那么餐厅,就可以作为生产这些产品的工厂
public class RestaurantFactory {
public Food getFood(String type) {
if (type == null)
return null;
else if (type.equalsIgnoreCase("hamburger"))
return new Hamburger();
else if (type.equalsIgnoreCase("steak"))
return new Steak();
return null;
}
}
有了餐厅工厂以后,客户下单就不需要自己去new食物了。直接问餐厅拿!
public static void main(String[] args) {
// 普通创建对象
Hamburger hamburger = new Hamburger();
Steak steak = new Steak();
hamburger.eat();
steak.eat();
// 利用简单工厂创建对象
RestaurantFactory rpf = new RestaurantFactory();
Food hamburger = rpf.getFood("hamburger");
Food steak = rpf.getFood("steak");
hamburger.eat();
steak.eat();
}
上面是普通创建,下面是简单工厂创建,最后会输出2次
吃汉堡,嘎嘎嘎
吃牛排,嘎嘎嘎
上面我们可以看到,我们把创建对象都写进了一个方法里。其实我们也可以分开写,一个方法获得一种对象也可以,看具体需求。
public class RestaurantFactory {
public Food getHamburger() {
return new Hamburger();
}
public Food getSteak() {
return new Steak();
}
}
静态工厂
静态工厂只是在普通工厂上做一些小小的改变,意味着,工厂里的创建对象方法改写为静态方法,如此以来,就不需要new Factory 再操作工厂生成对象了。
public class RestaurantFactory {
public static Food getHamburger() {
return new Hamburger();
}
public static Food getSteak() {
return new Steak();
}
}
public static void main(String[] args) {
// 利用静态工厂创建对象
Food hamburger = RestaurantFactory.getHamburger();
Food steak = RestaurantFactory.getSteak();
hamburger.eat();
steak.eat();
}
简单工厂模式的缺点
每次需要创建新的对象的时候,就得去修改工厂类,这明显违背了面向对象设计中的“开闭原则”。
工厂方法模式
工厂方法在简单工厂上做了抽象处理。
把具体工厂抽象出了一个超级工厂,所有的具体工厂都实现这个抽象的超级工厂。客户端通过访问具体工厂的工厂方法来创建产品。
结构
工厂方法模式的主要角色如下。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
在例子中实现
// 抽象工厂
public abstract class FoodAbstractFactory {
public abstract Food getFood();
}
// 具体工厂 麦当劳, Hamburger实现Food接口
public class McDonaldFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Hamburger();
}
}
// 具体工厂 咖啡厅, Steak实现Food接口
public class CafeFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Steak();
}
}
// Main函数
public static void main(String[] args) {
// 工厂方法创建对象
FoodAbstractFactory mcDonald = new McDonaldFactory();
FoodAbstractFactory cafe = new CafeFactory();
Food hamburger = mcDonald.getFood();
Food steak = cafe.getFood();
hamburger.eat();
steak.eat();
}
适用场景
工厂方法模式通常适用于以下场景。
- 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
- 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
- 客户不关心创建产品的细节,只关心产品的品牌。
优缺点
工厂方法模式的主要优点有:
- 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
- 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
其缺点是:
- 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
模式的扩展
当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式
抽象工厂模式
介绍
前面介绍的工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机、计算机软件学院只培养计算机软件专业的学生等。
同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。
本节要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族
举例解释
抽象工厂的意义是确实比较抽象。。。它主要用于创建 对象家族 。下面举例说明,更好理解它主要解决的问题。
比如我现在手机上安装了一款App,我想在这个App上实现换肤功能。(包括配色和ui风格)
那么,我们就可以理解为,换肤功能就是一个抽象工厂,而功能中提供的每一种皮肤,就是一个具体工厂类。
每当我们修改了皮肤主题,一般都会需要我们重启App,于是App会根据已选的皮肤主题,找到对应的具体皮肤主题工厂,进行UI初始化,包括ui配色,button风格,slider特效等等......
在以上的换肤过程里,我们可以发现,皮肤主题工厂最终初始化出来的对象们都是不同类的,比如 Color,Button,Slider,Bar,Table。那么以上这些类实例就可以看作是主题工厂产生的对象家族。
在例子中实现
上面的解释如果不太明白,就直接看代码吧。还是餐厅例子。餐厅除了食物,还会生产饮品,餐具等等。也就是说,食物,饮品和餐具就是餐厅抽象工厂定义的对象家族。看代码
// 抽象餐厅工厂
public abstract class RestaurantFactory {
public abstract Food getFood();
public abstract Drinks getDrinks();
public abstract Item getItem();
}
// 具体工厂 麦当劳, Hamburger实现Food接口,Cock实现Drinks,Tissue实现Item
public class McDonaldFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Hamburger();
}
@Override
public Drinks getDrinks() {
return new Cock();
}
@Override
public Item getItem() {
return new Tissue();
}
}
// 具体工厂 咖啡厅, Steak实现Food接口,Coffee实现Drinks,Knife实现Item
public class CafeFactory extend FoodAbstractFactoty {
@Override
public Food getFood() {
return new Steak();
}
@Override
public Drinks getDrinks() {
return new Coffee();
}
@Override
public Item getItem() {
return new Knife();
}
}
// Main函数
public static void main(String[] args) {
// 抽象工厂创建对象
FoodAbstractFactory mcDonald = new McDonaldFactory();
FoodAbstractFactory cafe = new CafeFactory();
Food hamburger = mcDonald.getFood();
Drinks cock = mcDonald.getDrinks();
Item tissue = mcDonald.getItem();
Food steak = cafe.getFood();
Drinks coffee = cafe.getDrinks();
Item knife = cafe.getItem();
hamburger.eat();
steak.eat();
cock.drink();
coffee.drink();
tissue.use();
knift.use();
}
模式的应用场景
抽象工厂模式最早的应用是用于创建属于不同操作系统的视窗构件。如 java 的 AWT 中的 Button 和 Text 等构件在 Windows 和 UNIX 中的本地实现是不同的。
抽象工厂模式通常适用于以下场景:
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
模式的扩展
抽象工厂模式的扩展有一定的“开闭原则”倾斜性:
- 当增加一个新的产品族时只需增加一个新的具体工厂,不需要修改原代码,满足开闭原则。
- 当产品族中需要增加一个新种类的产品时,则所有的工厂类都需要进行修改,不满足开闭原则。
另一方面,当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。