предисловие
Паттерны проектирования встречаются и на собеседованиях, и на работе, но я часто сталкиваюсь с небольшими партнерами, которые жалуются, что возможности применить шаблоны проектирования в реальной работе очень малы.
Недавно случайно встретил одного на работе.观察者模式Сцена решения проблемы доступна для всех.
Фон выглядит следующим образом:
Есть несколько дополнительных вещей, которые необходимо сделать в стандартном процессе, когда пользователь завершил создание заказа:
При этом эти бизнесы не фиксированы, а логика будет добавляться и изменяться в любое время по мере развития бизнеса.
Если логика прямо прописана в бизнесе заказа, то это”坨“Бизнесы, которые не являются очень основными, будут занимать все больше и больше, и модификация также может повлиять на обычный процесс заказа.
Конечно, есть и другие решения, например, можно запускать несколько задач по расписанию, регулярно сканировать и сканировать заказы, а потом реализовывать свою бизнес-логику, но при этом будет потрачено много ненужных запросов.
Шаблон наблюдателя
Следовательно, режим наблюдателя вошел. Это уведомление издателя событий, когда его собственные изменения государства, и наблюдатель получает сообщение для реализации бизнес-логики.
Таким образом, издатель и получатель события могут быть полностью отделены друг от друга, не влияя друг на друга, что по сути также является реализацией принципа открытия-закрытия.
образец кода
Давайте в общих чертах рассмотрим интерфейсы и отношения, используемые шаблоном наблюдателя:
- Основной интерфейс: определяет реализацию регистрации, круговой интерфейс уведомления.
- Интерфейс наблюдателя: определяет интерфейс для получения уведомлений от субъекта.
- И субъект, и интерфейс наблюдателя могут иметь несколько реализаций.
- Бизнес-код должен использовать только для использования
Subject.Nofity()интерфейс.
Рассмотрим пример реализации в процессе создания заказа.
Код реализован на go, и другие языки аналогичны.
Во-первых, два интерфейса определены в соответствии с приведенным выше рисунком:
type Subject interface {
Register(Observer)
Notify(data interface{})
}
type Observer interface {
Update(data interface{})
}
Поскольку это событие, которое размещает заказ, мы определяемOrderCreateSubjectвыполнитьSubject:
type OrderCreateSubject struct {
observerList []Observer
}
func NewOrderCreate() Subject {
return &OrderCreateSubject{}
}
func (o *OrderCreateSubject) Register(observer Observer) {
o.observerList = append(o.observerList, observer)
}
func (o *OrderCreateSubject) Notify(data interface{}) {
for _, observer := range o.observerList {
observer.Update(data)
}
}
один из нихobserverListСрезы используются для хранения всех наблюдателей, подписанных на событие упорядочения.
Следующим шагом является написание бизнес-логики наблюдателя Здесь я реализовал два:
type B1CreateOrder struct {
}
func (b *B1CreateOrder) Update(data interface{}) {
fmt.Printf("b1.....data %v \n", data)
}
type B2CreateOrder struct {
}
func (b *B2CreateOrder) Update(data interface{}) {
fmt.Printf("b2.....data %v \n", data)
}
Он также очень прост в использовании:
func TestObserver(t *testing.T) {
create := NewOrderCreate()
create.Register(&B1CreateOrder{})
create.Register(&B2CreateOrder{})
create.Notify("abc123")
}
Вывод:
b1.....data abc123
b2.....data abc123
- Создавать
创建订单предметsubject. - Зарегистрируйте все события подписки.
- Вызывается, когда требуется уведомление
Notifyметод.
Таким образом, когда нам нужно изменить реализацию каждого события, это не повлияет друг на друга, даже если мы захотим добавить другие реализации, это очень просто:
- Напишите класс реализации.
- Зарегистрируйтесь в организации.
Основной процесс не будет изменен.
подходит контейнер
На самом деле мы можем также опустить шаг регистрации событий, то есть использование контейнеров, общий процесс выглядит следующим образом:
- Все пользовательские события внедряются в контейнер.
- При повторной регистрации событий все события вынимаются из контейнера и регистрируются одно за другим.
Здесь используется контейнерgithub.com/uber-go/dig
В измененном коде всякий раз, когда мы добавляем наблюдателя (подписку на событие), нам нужно использовать толькоProvideФункция может быть зарегистрирована в контейнере.
В то же время, чтобы контейнер поддерживал несколько экземпляров одного и того же объекта, необходимо добавить некоторый код:
Observer.go:
type Observer interface {
Update(data interface{})
}
type (
Instance struct {
dig.Out
Instance Observer `group:"observers"`
}
InstanceParams struct {
dig.In
Instances []Observer `group:"observers"`
}
)
существуетobserverВ интерфейс необходимо добавить две новые структуры для хранения нескольких экземпляров одного и того же интерфейса.
group:"observers"Используется для объявления того же интерфейса.
Возвращается при создании конкретного объекта-наблюдателяInstanceобъект.
func NewB1() Instance {
return Instance{
Instance: &B1CreateOrder{},
}
}
func NewB2() Instance {
return Instance{
Instance: &B2CreateOrder{},
}
}
На самом деле он один раз заворачивается в Instance.
Таким образом, при регистрации наблюдателя вы можете получить к нему доступ изInstanceParams.InstancesУдалите все объекты-наблюдатели из .
err = c.Invoke(func(subject Subject, params InstanceParams) {
for _, instance := range params.Instances {
subject.Register(instance)
}
})
Таким образом, при использовании получайте объект топика прямо из контейнера, а затем уведомляйте:
err = c.Invoke(func(subject Subject) {
subject.Notify("abc123")
})
Для получения дополнительной информации об использовании dig обратитесь к официальной документации:
Суммировать
Опытные разработчики найдут и опубликуют очень похожую модель подписки, конечно, их идея похожа, мы не путаемся с разницей между ними (за исключением интервью), узнаем, какие идеи важнее.