文章

设计模式-状态模式

应用场景

当执行同一种操作时基于不同的状态出现不同的效果.

比如在画图软件中, 如果选中了画笔, 按住鼠标在画布上拖动就会绘制线条.

如果选中了选择工具, 按住鼠标在画布上拖动就会出现一个选择框.

对于鼠标的操作行为是相同的, 但是基于选中工具的状态不同, 则会执行不同的操作.

基础实现方式

可以创建一个枚举类, 用来列出所有的工具类型, 然后让画布包含一个枚举成员, 并在操作时判断这个成员的值.

classDiagram
    direction LR
    class Canvas{
        - ToolType
        + operation()
    }
    class ToolType{
        <<enumration>>
        SELECT,
        BRUSH,
        ERASER
    }
    Canvas *-- ToolType

简单模拟代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class ToolType(Enum):
    SELECT = 1
    BRUSH = 2
    ERASE = 3


class Canvas:

    def __init__(self, tool_type=ToolType.SELECT):
        self.__tool_type = tool_type

    def operation(self):
        if self.__tool_type == ToolType.SELECT:
            print("using select tool ")
        elif self.__tool_type == ToolType.BRUSH:
            print("using brush tool ")
        elif self.__tool_type == ToolType.ERASE:
            print("using erase tool ")

if __name__ == "__main__":
    canvas = Canvas()
    canvas.operation()
    canvas.tool_type = ToolType.BRUSH
    canvas.operation()            

但问题显而易见, 当工具增多时既要维护大量的枚举类型, 还要维护大量的枚举判断, 使得代码难以阅读, 极大的增加了维护成本.

使用状态设计模式解决

状态模式的本质是通过运行时的多态来完成不同状态下的不同操作.

classDiagram
    class Canvas{
        curreentTool
        +operation()
    }

    class Tool{
        <<interface>>
        +operation()
    }

    class SelectTool{
        +operation()
    }

    class BrushTool{
        +operation()
    }

    Canvas *-- Tool
    SelectTool ..|> Tool
    BrushTool ..|> Tool

如此一来, 如果在任何时候希望添加一个新的工具, 直接创建一个新的类来实现Tool接口即可以完成对程序的扩展, 而无需再修改原有的代码逻辑.

事实上, 所有需要通过类似select...case或者大量if...else if判断的场景都可以通过状态模式来解决.

状态设计模式, 也是OCP(Open Closed Principle)的一种体现, 即对扩展开放, 但是对修改关闭.

  • open for extension
  • closed for modification

简单的python实现代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import abc


class Tool(abc.ABC):

    @abc.abstractmethod
    def operation(self):
        pass


class SelectTool(Tool):

    def operation(self):
        print("select something...")


class BrushTool(Tool):

    def operation(self):
        print("brush something...")


class Canvas:

    def __init__(self, tool=None):
        self.__tool = tool or SelectTool()

    @property
    def tool(self):
        return self.__tool.__str__

    @tool.setter
    def tool(self, value: Tool):
        self.__tool = value

    def operation(self):
        self.__tool.operation()


if __name__ == "__main__":
    canvas = Canvas()
    canvas.operation()
    canvas.tool = BrushTool()
    canvas.operation()
本文由作者按照 CC BY 4.0 进行授权