题目
: Теперь, когда у вас есть число, средство форматирования по умолчанию должно отображать это значение в десятичном формате, но вам необходимо предоставить функцию, которая поддерживает добавление/регистрацию дополнительных средств форматирования (например: добавление средства форматирования шестнадцатеричного и двоичного форматирования). Каждый раз, когда значение обновляется, зарегистрированная программа уведомляется и отображает обновленное значение.
Давайте посмотрим на требования:
- NumberFormatter имеет числовое свойство
- При изменении числового значения следует изменить соответствующий метод форматирования для отображения результата.
- Система должна быть расширяемой, чтобы можно было использовать другие форматы.
Неправильная реализация может выглядеть так:
class NumberFormatter(object):
def __init__(self, number):
self.number = number
def show_data(self):
self.default_formatter()
self.hex_formatter()
self.binary_formatter()
def default_formatter(self):
pass
def hex_formatter(self):
pass
def binary_formatter(self):
pass
Мы можем использовать это так:
number = NumberFormatter(10)
number.show_data()
Но с этим есть проблема:这种针对实现的编程会导致我们在增加或者删除需要格式化方式时必须修改代码。
Например, нам больше не нужно отображение шестнадцатеричного формата чисел, нужно поставитьhex_formatter
Соответствующий код удален или закомментирован.
Для решения этой проблемы мы можем использовать观察者模式
.
Что такое шаблон наблюдателя
Знакомство с паттерном наблюдателя
Начнем с того, что происходит с подпиской на газеты и журналы:
- Дело газеты - издавать газеты
- Подпишитесь на газету, и пока у них есть новая газета, она будет доставлена вам, и пока вы являетесь их подписчиком, вы всегда будете получать новую газету.
- Если вы не хотите больше читать, отпишитесь, и вам не будут присылать новые статьи.
- Пока газеты работают, всегда найдутся люди, подписавшиеся на них или отписавшиеся от них.
Покажем это графически, здесь出版者
переименован主题(Subject)
,订阅者
переименован观察者(Observer)
:
1.
Вначале объект-утка не является наблюдателем.
2.
Объект-утка приходит, чтобы сообщить субъекту, что он хочет быть наблюдателем (на самом деле утка хочет сказать: я заинтересован в ваших изменениях данных, пожалуйста, сообщите мне, если будут изменения)3.
Объект утки уже является наблюдателем (утка ждет уведомления и, получив уведомление, получает целое число).4.
В топике появились новые данные (теперь утка и все остальные наблюдатели уведомлены:主题已经改变
)5.
Субъект-крыса попросил удалить его из числа наблюдателей (крыса слишком долго наблюдала за подтемой и решила больше не быть наблюдателем).6.
Крыса ушла (испытуемый, зная просьбу крысы, удалил ее от наблюдателя).7.
Субъект имеет новое целое число (все наблюдатели, кроме крысы, будут уведомлены, и если крыса захочет снова стать наблюдателем, она может вернуться)Определите шаблон наблюдателя
Когда вы пытаетесь набросать шаблон наблюдателя, воспользуйтесь услугами подписки на газеты, а также публикацией этого и подписчиками, чем вы все это делаете. В программировании шаблон наблюдателя обычно определяется как:
观察者模式
Определяет зависимость «один ко многим» между объектами, чтобы при изменении состояния объекта все его зависимые объекты получали уведомление и автоматически обновлялись.
Сравним с предыдущим примером:
Предметы и наблюдатели определяют однозначные отношения. Наблюдатели зависят от этой темы и будут уведомлены всякий раз, когда состояние темы изменяется. В зависимости от стиля уведомления, наблюдатель может быть обновлен с помощью этой новой стоимости.
现在你可能有疑问,这和一对多的关系有何关联?
В шаблоне Observer субъекты — это объекты, которые имеют состояния и могут управлять этими состояниями. То есть есть
一个
Тема со статусом. Наблюдатели же используют эти состояния, хотя эти состояния им не принадлежат. Есть много наблюдателей, которые полагаются на субъекта, чтобы сообщить им, когда состояние изменилось. Это создает отношения:一个主题对多个观察者的关系
.
观察者和主题之间的依赖关系是如何产生的?
Субъект — это тот, кто на самом деле владеет данными, а наблюдатель — его иждивенец, обновляющийся при изменении данных, что приводит к более чистому объектно-ориентированному дизайну, чем наличие множества объектов, управляющих одними и теми же данными.
Случаи использования шаблона наблюдателя
Режим наблюдателя имеет много случаев в практических приложениях, таких как агрегирование информации. Независимо от RSS, атом по-прежнему другой, идея одинакова: вы следуете источнику, когда он обновляется, вы получаете уведомления об обновлении.
Система, управляемая событиями, является примером использования шаблона Observer. В этой системе прослушиватели используются для прослушивания определенных событий. События прослушивателя запускаются при их создании. Это событие можно использовать для ввода определенной клавиши, перемещения мыши и т. д. События играют роль издателей, а слушатели играют роль наблюдателей.
Реализация Python
Теперь вернемся к вопросу в начале статьи.
Здесь мы можем реализовать базовый класс Publisher, включая общедоступные функции, такие как добавление, удаление и уведомление наблюдателей. Класс DefaultFormatter наследуется от Publisher и добавляет функции форматирования.
Код издателя выглядит следующим образом:
import itertools
'''
观察者模式实现
'''
class Publisher:
def __init__(self):
self.observers = set()
def add(self, observer, *observers):
for observer in itertools.chain((observer, ), observers):
self.observers.add(observer)
observer.update(self)
def remove(self, observer):
try:
self.observers.discard(observer)
except ValueError:
print('Failed to remove: {}'.format(observer))
def notify(self):
[observer.update(self) for observer in self.observers]
Теперь любая модель или класс, которые намереваются использовать шаблон Observer, должны расширять класс Publisher. Этот класс использует набор для хранения объекта-наблюдателя. Когда пользователь регистрирует новый объект наблюдателя в издателе, выполняется метод наблюдателя update(), который позволяет ему инициализировать себя с текущим состоянием модели. Когда состояние модели изменяется, следует вызвать унаследованный метод notify(), поэтому будет выполняться метод update() каждого объекта-наблюдателя, чтобы гарантировать, что все они отражают последнее состояние модели.
add()
Стоит отметить, что метод написан здесь для поддержки одного или нескольких объектов-наблюдателей. Здесь мы используемitertools.chain()
метод, который принимает любое количествоiterable
, и возвращает одноiterable
. Обход этой итерации эквивалентен последовательному обходу итераций в параметрах.
ДалееDefaultFomatter
Добрый.__init__()
Первое, что нужно сделать, это вызвать базовый класс__init__()
метод, так как это не может быть сделано автоматически в Python.DefaultFormatter
Экземпляры имеют собственные имена, чтобы мы могли отслеживать их состояние. для_data
переменная, мы использовали изменение имени, чтобы объявить, что переменная не может быть доступна напрямую.DefaultFormatter
Пучок_data
Переменная используется как целое число со значением по умолчанию 0.
class DefaultFormatter(Publisher):
def __init__(self, name):
Publisher.__init__(self)
self.name = name
self._data = 0
def __str__(self):
return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data)
@property
def data(self):
return self._data
@data.setter
def data(self, new_value):
try:
self._data = int(new_value)
except ValueError as e:
print('Error: {}'.format(e))
else:
self.notify()
-
__str__()
метод возвращает информацию об имени издателя и_data
ценная информация. type(self).__name — удобный способ получить имя класса, избегая жесткого кодирования имени класса. (Однако это снижает читаемость кода) -
data()
Есть два метода, первый использует@property
Декоратор для предоставления доступа на чтение к переменной _data. Таким образом, мы можем использоватьobject.data
заменитьobject._data
. Второй метод data() использует@setter
Декоратор, изменение декоратора будет использовать оператор присваивания (=) каждый раз, когда_data
Вызывается, когда переменной присваивается значение. Метод также пытается привести новое значение к целому числу и обрабатывает исключения, если преобразование завершается неудачно.
Далее добавляют наблюдатели.HexFormatter
а такжеBinaryFormatter
Функции в основном схожи. Единственная разница заключается в том, как форматируются значения данных, полученные от издателя, т.е. шестнадцатеричный и двоичный форматы.
class HexFormatter:
def update(self, publisher):
print("{}: '{}' has now hex data= {}".format(type(self).__name__,
publisher.name, hex(publisher.data)))
class BinaryFormatter:
def update(self, publisher):
print("{}: '{}' has now bin data= {}".format(type(self).__name__,
publisher.name, bin(publisher.data)))
Далее добавим тестовые данные и запустим код, чтобы посмотреть результаты:
def main():
df = DefaultFormatter('test1')
print(df)
print()
hf = HexFormatter()
df.add(hf)
df.data = 3
print(df)
print()
bf = BinaryFormatter()
df.add(bf)
df.data = 21
print(df)
print()
df.remove(hf)
df.data = 40
print(df)
print()
df.remove(hf)
df.add(bf)
df.data = 'hello'
print(df)
print()
df.data = 4.2
print(df)
if __name__ == '__main__':
main()
Полная ссылка на код:gist.GitHub.com/goosby/93ah0…
Запустите код:
python observer.py
## output
DefaultFormatter: 'test1' has data = 0
HexFormatter: 'test1' has now hex data= 0x0
HexFormatter: 'test1' has now hex data= 0x3
DefaultFormatter: 'test1' has data = 3
BinaryFormatter: 'test1' has now bin data= 0b11
BinaryFormatter: 'test1' has now bin data= 0b10101
HexFormatter: 'test1' has now hex data= 0x15
DefaultFormatter: 'test1' has data = 21
BinaryFormatter: 'test1' has now bin data= 0b101000
DefaultFormatter: 'test1' has data = 40
BinaryFormatter: 'test1' has now bin data= 0b101000
Error: invalid literal for int() with base 10: 'hello'
DefaultFormatter: 'test1' has data = 40
BinaryFormatter: 'test1' has now bin data= 0b100
DefaultFormatter: 'test1' has data = 4
Видим в выводе, добавляем дополнительных наблюдателей, будет больше вывода, после удаления вьювер больше не будет оповещения.
Суммировать
В этой статье мы представили принцип шаблона наблюдателя и реализацию кода Python. В реальной разработке проекта шаблон наблюдателя широко используется в программировании графического интерфейса пользователя, а также может использоваться в других архитектурах обработки времени, таких как моделирование и сервер, например:数据库触发器
,Django 的信号系统
,Qt GUI 应用程序框架的信号(signal)与槽(slot)机智
так же какWebSocket
многих вариантов использования.
Ссылка на ссылку
Наконец, поблагодарите мою подругу за ее поддержку.
Добро пожаловать в подписку (April_Louisa) | купи мне фанту |
---|---|