Keynote 原件地址

设计模式的精髓在意(意图)不在形(结构)!

意图是封装的依据,即变化点是什么,为什么要封装。而结构只是封装的结果。

举一个非常简单的例子:工厂方法模式

实际上,网上的工厂方法模式示例基本都是错的

如果你搜索工厂方法模式的示例,大概率会得到这样的示例。

// 首先,定义Shape接口:
public interface Shape { 
	void draw(); 
}
 
// 接着,实现两个具体的形状类:
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle.");
    }
}
 
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Rectangle.");
    }
}
 
// 创建一个抽象工厂类,它有一个抽象的工厂方法:
public abstract class ShapeFactory {
    // 工厂方法
    public abstract Shape getShape();
}
 
// 接下来,创建工厂的具体实现,该实现在工厂方法中基于给定的信息创建并返回实际的对象:
public class CircleFactory extends ShapeFactory {
 
    @Override
    public Shape getShape() {
        return new Circle();
    }
}
public class RectangleFactory extends ShapeFactory {
 
    @Override
    public Shape getShape() {
        return new Rectangle();
    }
}
 
// 使用`ConcreteShapeFactory`来获取形状对象,并调用它们的方法
public class FactoryPatternDemo {
    public static void main(String[] args) {
        // 获取 Circle 对象,并调用它的 draw 方法
        ShapeFactory shapeFactory = new CircleFactory();
        Shape shape1 = shapeFactory.getShape();
        shape1.draw();
 
        // 获取 Rectangle 对象,并调用它的 draw 方法
        shapeFactory = new RectangleFactory();
        Shape shape2 = shapeFactory.getShape();
        shape2.draw();
    }
}

在上面的示例中,每个Shape都需要一个对应的Factory,类增加了好多,带来了什么优势,一点都看不出来。实际上,这个示例并不是工厂方法模式。

工厂方法模式的意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

这里有个关键点:「让子类决定实例化哪一个类」。上面的示例其实完全没有体现出来。

还是以上面的例子来演示,标准的工厂方法模式应该是这样子的:

// Shape接口和实现类不变
 
// 创建一个类,它有一个抽象的工厂方法:
public abstract class ShapeShow {
    // 工厂方法
    public abstract Shape getShape();
 
	public void show() {
		Shape shape = getShape();
		shape.draw();
	}
}
 
// 接下来,创建工厂的具体实现,该实现在工厂方法中基于给定的信息创建并返回实际的对象:
public class CircleShow extends ShapeShow {
 
    @Override
    public Shape getShape() {
        return new Circle();
    }
}
 
public class RectangleShow extends ShapeShow {
 
    @Override
    public Shape getShape() {
        return new Rectangle();
    }
}
 
// 使用`ConcreteShapeFactory`来获取形状对象,并调用它们的方法
public class FactoryPatternDemo {
    public static void main(String[] args) {
        // 展示Circle
        ShapeShow shapeShow = new ShapeShow();
        shapeShow.show();
 
        // 展示 Rectangle 
        shapeShow = new RectangleShow();
        shapeShow.show();
    }
}

能看出来两个示例之间的区别吗?从结构上看,只是把main方法中的部分代码挪到了ShapeShow类中。而这恰恰就是关键点,ShapeShow这个类的核心作用并不是创建某个类,它是具体执行逻辑的类,只是提供了一个工厂方法,这个方法的具体实现由其子类来实现。而这才是工厂方法的意图:

  • ShapeShow这个父类并不决定实例化哪个Shape
  • 具体实例化哪个Shape,由CircleShow和RectangleShow这两个子类自行决定

也就是说,工厂方法通过抽象方法来解决这样一个变化点:「在执行逻辑中,需要使用的对象会发生变化」。

而如果要解决这样一个变化点:「在执行逻辑中,某些逻辑会发生变化」。你觉得应该用哪个设计模式呢?