工厂模式
安卓开发中的工厂模式
一、基本概念
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式分为三类:简单工厂模式、工厂方法模式和抽象工厂模式。它们之间的区别如下表所示:
工厂模式 | 特点 | 优点 | 缺点 |
---|---|---|---|
简单工厂模式 | 一个工厂类根据传入的参数决定创建出哪一种产品类的实例 | 简单易用,客户端无需知道具体产品类的名称,只需知道参数即可 | 不符合开闭原则,每次增加新产品时,都需要修改工厂类代码,增加了系统复杂度和具体类的依赖 |
工厂方法模式 | 定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中 | 符合开闭原则,每个具体产品都对应一个具体工厂类,不需要修改原有代码,只需增加新的具体产品和对应的具体工厂 | 类的个数容易过多,增加了系统的抽象性和理解难度 |
抽象工厂模式 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类² | 符合开闭原则,隔离了具体类的生成,客户端不需要知道产品内部结构和实现细节 | 难以扩展新种类的产品,需要修改抽象工厂和所有相关的子类 |
1.1 简单工厂模式
简单工厂模式uml图简单工厂模式又叫静态工厂方法模式,它是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
简单工厂模式的优点是:
- 工厂类包含了必要的逻辑判断,根据客户端的选择动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。
- 客户端无需知道所创建具体产品的类名,只需要知道参数即可。
- 通过引入配置文件,可以在不修改客户端代码的情况下更换和增加新的具体产品类。
简单工厂模式的缺点是:
- 工厂类的职责过重,增加新的产品时需要修改工厂类的判断逻辑,违背了开闭原则。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,可能造成工厂逻辑过于复杂。
- 简单工厂模式使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
1.2 工厂方法模式
工厂方法模式uml图工厂方法模式是一种创建型设计模式,它定义了一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。这样可以使得客户端不需要知道具体的产品类,只需要知道工厂类即可。工厂方法模式可以让工厂根据不同的条件创建不同的产品对象,而无需修改工厂类的代码。
工厂方法模式的优点是:
- 符合开闭原则,增加新的产品时,只需要增加新的具体产品类和对应的具体工厂类,无需修改原有的代码。
- 符合单一职责原则,每个具体工厂类只负责创建对应的产品。
- 符合依赖倒置原则,抽象工厂和抽象产品为稳定的抽象层,而具体工厂和具体产品为可变的实现层。
- 客户端与具体产品解耦,客户端只需要知道产品的抽象接口和工厂的抽象接口。
工厂方法模式的缺点是:
- 类的个数增多,每增加一个产品就要增加一个具体产品类和一个具体工厂类,增加了系统的复杂度。
- 抽象工厂中只能有一个抽象产品,如果需要创建多个抽象产品,就需要使用抽象工厂模式。
- 增加了系统的抽象性和理解难度。
1.3 抽象工厂模式
抽象工厂模式uml图抽象工厂模式是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式可以将一组对象的实现细节与他们的一般使用分离开来。
抽象工厂模式的优点是:
- 封装性好,代码结构清晰,降低了客户端与具体产品的耦合度。
- 扩展性好,增加一个产品族或者具体产品都很方便,符合开闭原则。
- 有利于产品的一致性,当一个系列的产品对象被设计成一起工作时,它能够保证客户端始终只使用同一个系列的产品对象。
抽象工厂模式的缺点是:
- 难以支持新种类的产品,如果需要增加一个新的产品族,就需要修改所有的工厂角色,包括抽象工厂类,在产品族较多时,可能造成工厂方法模式的代码非常复杂。
二、安卓源码中的实例
安卓源码中有很多使用了工厂模式的地方,例如:
-
LayoutInflater 类是一个用于将 XML 布局文件转换为 View 对象的类,它是一个抽象类,有三个子类:PhoneLayoutInflater、HwPhoneLayoutInflater 和 BridgeInflater。这三个子类分别对应不同的平台或场景,例如 PhoneLayoutInflater 是用于手机端的布局加载器,HwPhoneLayoutInflater 是华为定制的布局加载器,BridgeInflater 是用于 Android Studio 的布局加载器。这些子类都实现了 LayoutInflater 的抽象方法 onCreateView() ,用于根据不同的条件创建不同类型的 View 对象。这就是一个典型的工厂方法模式。
-
Intent 类是一个用于描述应用程序之间交互信息的类,它包含了组件名称、动作、数据、类别等信息。Intent 有一个静态方法 createChooser() ,它可以根据传入的 Intent 对象和标题创建一个选择器(Chooser)Intent 对象,用于弹出一个选择框让用户选择要启动哪个应用程序。这就是一个简单工厂模式。
-
SharedPreferences 类是一个用于存储和获取键值对数据的类,它有一个内部接口 SharedPreferencesImpl.EditorImpl ,它是 SharedPreferences.Editor 接口的实现类,用于编辑 SharedPreferences 中的数据。SharedPreferencesImpl.EditorImpl 有一个方法 createStorageOperation() ,它根据传入的参数创建不同类型的 StorageOperation 对象,例如 PutStringOperation、PutIntOperation 等。这些 StorageOperation 对象都实现了 Runnable 接口,用于在后台线程中执行数据存储操作。这也是一个工厂方法模式。
-
在 Activity 中,我们可以通过 setContentView 方法来设置布局文件,这个方法就可以看作是一个简单工厂方法,在其中我们将构造的 View 通过 setContentView 返回给 framework 处理,剩下的布局就由系统去创建。
-
在 LayoutInflater 类中,我们可以通过 inflate 方法来加载布局文件,并返回一个 View 对象。这个方法也可以看作是一个简单工厂方法,在其中我们根据布局文件中定义的控件类型来创建对应的 View 对象,并将它们组合成一个 View 树返回给客户端⁵。
-
在 android.graphics 包中,有一个 PathParser 类,它可以根据 SVG 格式的字符串来解析出一个 Path 对象。这个类也可以看作是一个简单工厂类,在其中我们根据字符串中定义的路径命令来创建对应的 Path 段,并将它们组合成一个 Path 对象返回给客户
现工厂模式的灵活性和可读性。
下面我们用 Kotlin 语言来实现和比较各种工厂模式的不同之处。
3.1 简单工厂模式
简单工厂模式可以用单例模式或伴生对象来实现,例如:
// 抽象产品类
interface Shape {
fun draw()
}
// 具体产品类
class Circle : Shape {
override fun draw() {
println("Draw a circle")
}
}
class Rectangle : Shape {
override fun draw() {
println("Draw a rectangle")
}
}
class Square : Shape {
override fun draw() {
println("Draw a square")
}
}
// 工厂类,使用单例模式
object ShapeFactory {
// 根据参数创建不同的产品对象
fun getShape(shapeType: String): Shape? {
return when (shapeType) {
"CIRCLE" -> Circle()
"RECTANGLE" -> Rectangle()
"SQUARE" -> Square()
else -> null
}
}
}
// 或者使用伴生对象
class ShapeFactory2 private constructor() {
companion object {
fun getShape(shapeType: String): Shape? {
return when (shapeType) {
"CIRCLE" -> Circle()
"RECTANGLE" -> Rectangle()
"SQUARE" -> Square()
else -> null
}
}
}
}
客户端可以通过工厂类的静态方法来获取产品对象,例如:
fun main() {
// 使用单例工厂
val circle = ShapeFactory.getShape("CIRCLE")
circle?.draw()
val rectangle = ShapeFactory.getShape("RECTANGLE")
rectangle?.draw()
val square = ShapeFactory.getShape("SQUARE")
square?.draw()
// 使用伴生对象工厂
val circle2 = ShapeFactory2.getShape("CIRCLE")
circle2?.draw()
val rectangle2 = ShapeFactory2.getShape("RECTANGLE")
rectangle2?.draw()
val square2 = ShapeFactory2.getShape("SQUARE")
square2?.draw()
}
输出结果:
Draw a circle
Draw a rectangle
Draw a square
Draw a circle
Draw a rectangle
Draw a square
3.2 工厂方法模式
工厂方法模式可以用高阶函数和 lambda 表达式来实现,例如:
// 抽象产品类
interface Shape {
fun draw()
}
// 具体产品类
class Circle : Shape {
override fun draw() {
println("Draw a circle")
}
}
class Rectangle : Shape {
override fun draw() {
println("Draw a rectangle")
}
}
class Square : Shape {
override fun draw() {
println("Draw a square")
}
}
// 抽象工厂类,使用高阶函数作为参数
abstract class FactoryMethod(val factory: () -> Shape) {
// 创建产品对象的方法
fun create(): Shape {
return factory()
}
}
// 具体工厂类,使用 lambda 表达式实现抽象工厂类的参数
class CircleFactoryMethod : FactoryMethod({ Circle() })
class RectangleFactoryMethod : FactoryMethod({ Rectangle() })
class SquareFactoryMethod : FactoryMethod({ Square() })
客户端可以通过具体工厂类的 create 方法来获取产品对象,例如:
fun main() {
// 使用圆形工厂创建圆形对象
val circleFactory = CircleFactoryMethod()
val circle = circleFactory.create()
circle.draw()
// 使用矩形工厂创建矩形对象
val rectangleFactory = RectangleFactoryMethod()
val rectangle = rectangleFactory.create()
rectangle.draw()
// 使用正方形工厂创建正方形对象
val squareFactory = SquareFactoryMethod()
val square = squareFactory.create()
square.draw()
}
输出结果:
Draw a circle
Draw a rectangle
Draw a square
3.3 抽象工厂模式
抽象工厂模式可以用扩展函数和运算符重载来实现,例如:
// 抽象产品类
interface Shape {
fun draw()
}
interface Color {
fun fill()
}
// 具体产品类
class Circle : Shape {
override fun draw() {
println("Draw a circle")
}
}
class Rectangle : Shape {
override fun draw() {
println("Draw a rectangle")
}
}
class Square : Shape {
override fun draw() {
println("Draw a square")
}
}
class Red : Color {
override fun fill() {
println("Fill red")
}
}
class Green : Color {
override fun fill() {
println("Fill green")
}
}
class Blue : Color {
override fun fill() {
println("Fill blue")
}
}
// 抽象工厂类
interface AbstractFactory {
// 创建形状对象的方法
fun getShape(shapeType: String): Shape?
// 创建颜色对象的方法
fun getColor(colorType: String): Color?
}
// 具体工厂类,用于创建形状对象
class ShapeFactory : AbstractFactory {
override fun getShape(shapeType: String): Shape? {
return when (shapeType) {
"CIRCLE" -> Circle()
"RECTANGLE" -> Rectangle()
"SQUARE" -> Square()
else -> null
}
}
override fun getColor(colorType: String): Color? {
return null
}
}
// 具体工厂类,用于创建颜色对象
class ColorFactory : AbstractFactory {
override fun getShape(shapeType: String): Shape? {
return null
}
override fun getColor(colorType: String): Color? {
return when (colorType) {
"RED" -> Red()
"GREEN" -> Green()
"BLUE" -> Blue()
else -> null
}
}
}
// 工厂生成器类,用于获取具体工厂类的对象
object FactoryProducer {
// 根据参数获取不同的工厂对象
fun getFactory(factoryType: String): AbstractFactory? {
return when (factoryType) {
"SHAPE" -> ShapeFactory()
"COLOR" -> ColorFactory()
else -> null
}
}
}
客户端可以通过工厂生成器类的方法来获取具体工厂类的对象,然后通过具体工厂类的方法来获取产品对象,例如:
fun main() {
// 获取形状工厂
val shapeFactory = FactoryProducer.getFactory("SHAPE")
// 获取形状为 Circle 的对象
val circle = shapeFactory?.getShape("CIRCLE")
circle?.draw()
// 获取形状为 Rectangle 的对象
val rectangle = shapeFactory?.getShape("RECTANGLE")
rectangle?.draw()
// 获取形状为 Square 的对象
val square = shapeFactory?.getShape("SQUARE")
square?.draw()
// 获取颜色工厂
val colorFactory = FactoryProducer.getFactory("COLOR")
// 获取颜色为 Red 的对象
val red = colorFactory?.getColor("RED")
red?.fill()
// 获取颜色为 Green 的对象
val green = colorFactory?.getColor("GREEN")
green?.fill()
// 获取颜色为 Blue 的对象
val blue = colorFactory?.getColor("BLUE")
blue?.fill()
}
输出结果:
Draw a circle
Draw a rectangle
Draw a square
Fill red
Fill green
Fill blue
四、使用场景
- 当一个类不知道它所需要的对象的类时,例如一个工厂方法可以根据参数来决定创建哪种产品对象。
- 当一个类希望通过其子类来指定创建对象时,例如一个抽象工厂可以让其子类来实现具体的创建逻辑。
- 当需要创建一组相关或相互依赖的对象时,例如一个抽象工厂可以创建一系列相同风格或主题的产品对象。
五、优化方向
简单工厂模式的缺点是:
- 工厂类的职责过重,增加新的产品时需要修改工厂类的判断逻辑,违背了开闭原则。
- 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,可能造成工厂逻辑过于复杂。
- 简单工厂模式使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
简单工厂模式的优化方法是:
- 使用反射机制,根据传入的参数来动态创建对象,而不是使用 switch-case 或 if-else 语句。
- 使用配置文件,将具体产品类的类名和参数映射起来,从而避免修改源代码。
- 使用工厂方法模式或抽象工厂模式,将工厂类抽象化,让子类来决定创建哪种产品对象。
// 优化简单工厂模式,使用反射机制
public class SimpleFactory {
// 根据类名创建对象
public static Object create(String className) {
Object obj = null;
try {
// 通过反射获取类对象
Class<?> clazz = Class.forName(className);
// 通过反射创建实例
obj = clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
}
工厂方法模式的缺点是:
- 类的个数增多,每增加一个新产品就要增加一个新产品族
和一个具体工厂类,增加了系统的复杂度。 - 抽象工厂中只能有一个抽象产品,如果需要创建多个抽象产品,就需要使用抽象工厂模式。
- 增加了系统的抽象性和理解难度。
工厂方法模式的优化方法是:
- 使用泛型,将具体产品类作为类型参数传递给工厂类,从而避免创建多个具体工厂类。
- 使用注解,将具体产品类标注为某种类型,然后通过反射机制来获取对应的产品对象,从而避免修改工厂类的逻辑。
- 使用简单工厂模式,将工厂方法模式中的具体工厂类合并为一个简单工厂类,从而减少类的个数。
// 优化工厂方法模式,使用泛型
public abstract class GenericFactory<T> {
// 创建产品对象的方法
public T create() {
T product = createProduct();
product.build();
return product;
}
// 抽象方法,由子类实现
protected abstract T createProduct();
}
// 具体工厂类,使用泛型参数指定具体产品类型
public class CircleFactory extends GenericFactory<Circle> {
@Override
protected Circle createProduct() {
return new Circle();
}
}
抽象工厂模式的缺点是:
- 类的个数增多,每增加一个产品族就要增加一个抽象产品和多个具体产品类,增加了系统的复杂度。
- 难以支持新种类的产品,如果需要增加一个新的产品等级结构,就需要修改所有的工厂角色,包括抽象工厂类,在产品族较多时,可能造成工厂方法模式的代码非常复杂。
- 增加了系统的抽象性和理解难度。
抽象工厂模式的优化方法是:
- 使用反射机制,根据传入的参数来动态创建对象,而不是使用 switch-case 或 if-else 语句。
- 使用配置文件,将具体产品类的类名和参数映射起来,从而避免修改源代码。
- 使用原型模式,将已经创建好的对象作为原型来复制新的对象,从而避免重复创建相同类型的对象。
// 优化抽象工厂模式,使用原型模式
public abstract class PrototypeFactory {
// 存储已经创建好的对象
private Map<String, Object> prototypes = new HashMap<>();
// 构造方法,初始化原型对象
public PrototypeFactory() {
initPrototypes();
}
// 抽象方法,由子类实现
protected abstract void initPrototypes();
// 根据类型获取对象的副本
public Object getPrototype(String type) {
Object prototype = prototypes.get(type);
if (prototype == null) {
throw new IllegalArgumentException("No such prototype: " + type);
}
// 使用 clone 方法复制对象
return prototype.clone();
}
// 添加或更新原型对象
public void addOrUpdatePrototype(String type, Object prototype) {
prototypes.put(type, prototype);
}
}
// 具体工厂类,初始化具体的原型对象
public class ShapeColorFactory extends PrototypeFactory {
@Override
protected void initPrototypes() {
addOrUpdatePrototype("CIRCLE", new Circle());
addOrUpdatePrototype("RECTANGLE", new Rectangle());
addOrUpdatePrototype("SQUARE", new Square());
addOrUpdatePrototype("RED", new Red());
addOrUpdatePrototype("GREEN", new Green());
addOrUpdatePrototype("BLUE", new Blue());
}
}