6.8. Mediator¶
EN: Mediator
PL: Mediator
Type: object
6.8.1. Pattern¶
Input fields which needs to collaborate
Cannot submit form if all required fields are not filled
If you select article in list of articles, editor form with current article content and title gets populated
Auto slug-field based on title content

6.8.2. Problem¶

6.8.3. Solution¶


from abc import ABC, abstractmethod
from dataclasses import dataclass
class DialogBox(ABC):
"""Mediator class"""
@abstractmethod
def changed(self, control: 'UIControl') -> None:
pass
@dataclass
class UIControl(ABC):
owner: DialogBox
class ListBox(UIControl):
selection: str
def __init__(self, owner: DialogBox) -> None:
super().__init__(owner)
def get_selection(self) -> str:
return self.selection
def set_selection(self, selection: str) -> None:
self.selection = selection
self.owner.changed(self)
class TextBox(UIControl):
content: str
def __init__(self, owner: DialogBox) -> None:
super().__init__(owner)
def get_content(self) -> str:
return self.content
def set_content(self, content: str) -> None:
self.content = content
self.owner.changed(self)
class Button(UIControl):
enabled: bool
def __init__(self, owner: DialogBox) -> None:
super().__init__(owner)
def set_enabled(self, enabled: bool) -> None:
self.enabled = enabled
def is_enabled(self) -> bool:
self.owner.changed(self)
return self.enabled
class ArticlesDialogBox(DialogBox):
articles_listbox: ListBox
title_textbox: TextBox
save_button: Button
def simulate_user_interaction(self) -> None:
self.articles_listbox.set_selection('Article 1')
self.title_textbox.set_content('')
self.title_textbox.set_content('Article 2')
print(f'Text box: {self.title_textbox.get_content()}')
print(f'Button: {self.save_button.is_enabled()}')
def __init__(self) -> None:
self.articles_listbox = ListBox(self)
self.title_textbox = TextBox(self)
self.save_button = Button(self)
def changed(self, control: 'UIControl') -> None:
if control == self.articles_listbox:
self.article_selected()
elif control == self.title_textbox:
self.title_changed()
def article_selected(self) -> None:
self.title_textbox.set_content(self.articles_listbox.get_selection())
self.save_button.set_enabled(True)
def title_changed(self) -> None:
content = self.title_textbox.get_content()
is_empty = (content == None or content == '')
self.save_button.set_enabled(not is_empty)
if __name__ == '__main__':
dialog = ArticlesDialogBox()
dialog.simulate_user_interaction()
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
class EventHandler(ABC):
@abstractmethod
def __call__(self) -> None:
pass
@dataclass
class UIControl(ABC):
observers: list[EventHandler] = field(default_factory=list)
def add_event_handler(self, observer: EventHandler) -> None:
self.observers.append(observer)
def _notify_event_handlers(self):
for observer in self.observers:
observer.__call__()
class ListBox(UIControl):
selection: str
def get_selection(self) -> str:
return self.selection
def set_selection(self, selection: str) -> None:
self.selection = selection
self._notify_event_handlers()
class TextBox(UIControl):
content: str
def get_content(self) -> str:
return self.content
def set_content(self, content: str) -> None:
self.content = content
self._notify_event_handlers()
class Button(UIControl):
enabled: bool
def set_enabled(self, enabled: bool) -> None:
self.enabled = enabled
self._notify_event_handlers()
def is_enabled(self) -> bool:
return self.enabled
@dataclass
class ArticlesDialogBox:
articles_listbox: ListBox = ListBox()
title_textbox: TextBox = TextBox()
save_button: Button = Button()
def __post_init__(self):
self.articles_listbox.add_event_handler(self.article_selected)
self.title_textbox.add_event_handler(self.title_changed)
def simulate_user_interaction(self) -> None:
self.articles_listbox.set_selection('Article 1')
self.title_textbox.set_content('')
self.title_textbox.set_content('Article 2')
print(f'Text box: {self.title_textbox.get_content()}')
print(f'Button: {self.save_button.is_enabled()}')
def article_selected(self) -> None:
self.title_textbox.set_content(self.articles_listbox.get_selection())
self.save_button.set_enabled(True)
def title_changed(self) -> None:
content = self.title_textbox.get_content()
is_empty = (content == None or content == '')
self.save_button.set_enabled(not is_empty)
if __name__ == '__main__':
dialog = ArticlesDialogBox()
dialog.simulate_user_interaction()
6.8.4. Assignments¶
"""
* Assignment: DesignPatterns Behavioral Mediator
* Complexity: medium
* Lines of code: 15 lines
* Time: 21 min
English:
1. Implement Mediator pattern
2. Create form with Username, Password and Submit button
3. If Username and Password are provided enable Submit button
4. Run doctests - all must succeed
Polish:
1. Zaimplementuj wzorzec Mediator
2. Stwórz formularz logowania z Username, Password i przyciskiem Submit
3. Jeżeli Username i Password odblokuj przycisk Submit
4. Uruchom doctesty - wszystkie muszą się powieść
Tests:
>>> form = LoginForm()
>>> form.set_username('root')
>>> form.set_password('')
>>> form.submit()
Traceback (most recent call last):
PermissionError: Cannot submit form without Username and Password
>>> form = LoginForm()
>>> form.set_username('root')
>>> form.set_password('MyVoiceIsMyPasswordVerifyMe')
>>> form.submit()
'Submitted'
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Any
@dataclass
class UIElement(ABC):
name: str
owner: Form
value: Any
def changed(self):
raise NotImplementedError
@abstractmethod
def set_value(self, value: Any) -> None: ...
@abstractmethod
def get_value(self) -> Any: ...
@dataclass
class Input(UIElement):
value: str = ''
def get_value(self) -> str:
raise NotImplementedError
def set_value(self, value: str) -> None:
raise NotImplementedError
@dataclass
class Button(UIElement):
value: bool = False
def set_value(self, value: bool) -> None:
raise NotImplementedError
def get_value(self) -> Any:
raise NotImplementedError
def enable(self):
self.set_value(True)
def disable(self):
self.set_value(False)
def is_enabled(self) -> bool:
return self.value
class Form(ABC):
@abstractmethod
def on_change(self): ...
class LoginForm(Form):
username_input: Input
password_input: Input
submit_button: Button
def __init__(self):
raise NotImplementedError
def set_username(self, username: str):
raise NotImplementedError
def set_password(self, password: str):
raise NotImplementedError
def on_change(self):
raise NotImplementedError
def submit(self):
if self.submit_button.is_enabled():
return 'Submitted'
else:
raise PermissionError('Cannot submit form without Username and Password')