Второй пример реализует то, что известно как Шаблон наблюдателя . Вы (наблюдатель) хотите получить уведомление о каком-либо событии, которое произошло в объекте X (наблюдаемом), верно? Чем вы скажете наблюдаемое, чтобы уведомить вас. Или, другими словами, вы начинаете слушать его, поэтому его также называют слушателем. The Observable не знает всех своих подписчиков. Он просто берет их и добавляет их в контейнер или коллекцию. Наблюдатель знает интерфейс IObserver
, который реализуют все наблюдатели, и поэтому знает, какой метод (обратный вызов) вызывать.
Первый пример не имеет смысла. Наблюдаемое вызывает метод для наблюдателя, чтобы заставить его подписаться? Этот дизайн должен быть странным.
Допустим, у вас есть класс FileWriter
, который выставляет событие FileWriter.Completed
. У вас есть другой класс FileHandler
, который управляет всеми файлами. FileHandler
звонит FileWriter.write(filePath, data)
. После каждой операции FileHandler
хочет показать сообщение пользователю. Таким образом, FileWriter is used by the
FileHandler , but without knowing who's calling the
write method. The
FileHandler , one of the many consumers of
FileWriter.write (string, string) , knows that
FileWriter реализует наблюдаемый интерфейс, называемый IWriteToFileCompletedEvent
. Поэтому он может быть уверен, что наблюдаемая имеет метод FileWriter.subscribeToWriteToFileCompleted(IObserver eventListener)
. Как вы хотите, чтобы FileWriter
знал обо всех потребителях его событий до того, как они подпишутся ?. Думаю об этом. Это было бы странным API, например: FileWriter.write(filepath, data, this)
, чтобы дать FileWriter
ссылку на слушателя. Затем FileWriter
принимает ссылку на слушателя и вызывает listener.subscribe(this)
. Теперь FileHandler
(слушатель) имеет бесполезную ссылку на FileWriter
(бесполезно, потому что он знает FileWriter
раньше, так как он вызвал FileWriter.write
на нем), поэтому слушатель принимает ссылку FileWriter
, которую он уже должен позвонить observable.getObservers().add(this)
. Я получил удар при наборе этого. Серьезно, это какие-то безумные спагетти.
Так будет выглядеть реальная реализация в «реальной жизни»
// The interface for the publisher or observable of a special event
interface IWriteToFileCompletedEvent {
void subscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer);
void unsubscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer);
}
// The interface for the observer of a special event
interface IWriteToFileCompletedListener {
void notify(string filePathOfCompletedFile);
}
// The observable that exposes a special event
class FileWriter implements IWriteToFileCompletedEvent {
private List<IWriteToFileCompletedListener> writeToFileCompletedListeners = new ArrayList<>();
public void subscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer) {
this.writeToFileCompletedListeners.add(observer);
}
public void unsubscribeToWriteToFileCompleted(IWriteToFileCompletedListener observer) {
this.writeToFileCompletedListeners.remove(observer);
}
public void writeToFile(string filePath, string data) {
// Write data to file
// Once done notify all listeners of the write completed event
for (IWriteToFileCompletedListener observer : this.writeToFileCompletedListeners) {
observer.notify(filePath);
}
}
}
// The observer of a special event
class ContactsHandler implements IWriteToFileCompletedListener {
private FileWriter fileWriter = new FileWriter();
public void saveUserContactToFile(string filePath, string userInput) {
this.fileWriter.subscribeToWriteToFileCompleted(this);
this.fileWriter.writeToFile(filePath, userInput);
}
// Implementation of interface IWriteToFileCompletedListener
public void notify(string filePath) {
this.fileWriter.unsubscribeToWriteToFileCompleted(this);
this.messageDialog.show("The new contact was successfully saved to " + filePath);
}
}
// Another observer of a special event
class SettingsHandler implements IWriteToFileCompletedListener {
private FileWriter fileWriter = new FileWriter();
public void saveUserSettingsToFile(string filePath, string userSettings) {
this.fileWriter.subscribeToWriteToFileCompleted(this);
this.fileWriter.writeToFile(filePath, userSettings);
}
// Implementation of interface IWriteToFileCompletedListener
public void notify(string filePath) {
this.fileWriter.unsubscribeToWriteToFileCompleted(this);
this.messageDialog.show("The new settings were successfully saved to " + filePath);
}
}
Классы будут взаимодействовать так
public void main(strng[] args {
SettingsHandler settingsHandler = new SettingsHandler();
ContactsHandler contactsHandler = new ContactsHandler();
// Imaging this method receives user input fromo the UI:
string newContact = textBox.gettext();
this.contactsHandler.saveUserContactToFile("C:\Contacts.txt", newContact);
// While waiting for the message to show the user adjusted some settings and clicked 'save'
string changedSettings = getChangedSettings();
this.settingsHandler.saveUserSettingsToFile("C:\UserSettings.txt", changedSettings);
// After a while the user sees the messages send from the event listeners.
}
Несколько объектов совершенно другого типа (SettingsHandler
и ContactsHandler
) подписались на одно и то же событие. Все чисто. Объект, который использует объект, который он хочет наблюдать, подписывается. Класс, который можно наблюдать, наблюдаемый, просто уведомляет всех подписчиков. вот и все.
Подводя итог: ваше решение 1 совсем не удобно (хотя вы можете заставить его работать) и выдает ужасный код.
Решение 2 фактически следует шаблону, который описывает, как реализовать события. Эта реализация доказана и хорошо зарекомендовала себя, поскольку она достигает цели введения событий и создает чистый и читаемый код.