文章

设计模式-中介模式

应用场景

通常用于多个元素之间的统一通信调度. 举例来说, 一个用户界面拥有多个类型的UI:

  • ListBox
  • TextBox
  • button

当选中listbox中的项时同步修改textbox中的文字. 而当textbox中包含文字时按钮可用, 当textbox中的文字为空时按钮不可用.

classDiagram
    note for ListBox "selected():<br/>textbox.setContent(...)"
    class ListBox{
        + selected()
    }
    note for TextBox "changed():<br/>button.setEnabled(...)"
    class TextBox{
        + changed()
    }
    note for Button "clicked():<br/>textbox.getContent(...)<br/>//update"
    class Button{
        + clicked()
    }

    ListBox --> TextBox : 数据交互
    ListBox --> Button : 状态控制
    TextBox --> Button : 状态控制
    Button --> TextBox: 数据交互

虽然功能不复杂, 但是相互之间的连线却是不少. 如果只是单纯的进行UI罗列, 同时直接在UI之间进行交互, 一旦UI数量增多, 就会变得相当难以维护.

中介模式便是为了应对这一问题, 通过一个中介类管理所有的交互状态, 使不同的UI之间彼此隔绝.

classDiagram
    class ListBox{
        + selected()
    }
    
    class TextBox{
        + changed()
    }
    
    class Button{
        + clicked()
    }

    class Madiator{
        + update()
    }

    ListBox <--> Madiator
    TextBox <--> Madiator
    Button <--> Madiator

任何一个UI的状态发生改变, 仅需要通知中介对象, 至于其他UI应该做什么对应的调整发生变动的UI本身不需要关心, 也就有了逻辑统一的管理和维护.

简单的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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from abc import ABC, abstractmethod


class DialogBox(ABC):

    @abstractmethod
    def update(self, src, payload=None):
        pass


class UIControl(ABC):

    __owner = None

    def __init__(self, name, title=None):
        self.is_enabled = True
        self.name = name
        self.title = title

    @abstractmethod
    def draw(self):
        pass

    @property
    def owner(self) -> DialogBox:
        return self.__owner

    @owner.setter
    def owner(self, value: DialogBox):
        self.__owner = value


class UIButton(UIControl):

    def draw(self):
        print("draw button")

    def click(self):
        if self.owner:
            self.owner.update(self)

        print("click button")


class UITextBox(UIControl):

    def __init__(self, name, content=None):
        self.__content = content
        super().__init__(name)

    def draw(self):
        print("draw text box")

    def text_changed(self):
        if self.owner:
            self.owner.update(self)
        print("text changed")

    @property
    def content(self):
        return self.__content

    @content.setter
    def content(self, value):
        self.__content = value
        self.text_changed()


class UIListBox(UIControl):

    def __init__(self, name, *items):
        self.__items = items
        super().__init__(name)
        self.__selected = -1

    def on_selected(self, index):
        print(f"selected item on index: {index}")
        if self.owner:
            self.owner.update(self)

    @property
    def selected(self):
        return None if self.__selected == -1 \
            else (self.__selected, self.items[self.__selected])

    @selected.setter
    def selected(self, value):
        if value > 0 and self.__selected != value:
            self.__selected = value
            self.on_selected(value)

    @property
    def items(self):
        return self.__items

    @items.setter
    def items(self, value):
        self.__items = value

    def draw(self):
        print("draw listbox")


class DemoPageDialog(DialogBox):

    def __init__(self, *ui_list):
        self.__ui_list = ui_list
        for ui in ui_list:
            if isinstance(ui, UIControl):
                ui.owner = self

    def update(self, src: UIControl, payload=None):
        if src not in self.__ui_list:
            return

        if isinstance(src, UIButton):
            print(">> save text to database...")
        elif isinstance(src, UITextBox):
            print(f">> textbox content change to {src.content}")
            print(">> set button enabled...")
        elif isinstance(src, UIListBox):
            print(f">> listbox selected on:{src.selected}")


if __name__ == "__main__":

    btn = UIButton("btn")
    txt = UITextBox("title")
    listbox = UIListBox("list", "title1", "title2", "title3")

    DemoPageDialog(btn, listbox, txt)

    listbox.selected = 1
    txt.content = "new content..."
    btn.click()

输出结果:

1
2
3
4
5
6
7
selected item on index: 1
>> listbox selected on:(1, 'title2')
>> textbox content change to new content...
>> set button enabled...
text changed
>> save text to database...
click button
本文由作者按照 CC BY 4.0 进行授权