Въведение в Null Object Pattern

1. Общ преглед

В този бърз урок ще разгледаме Null Object Pattern, специален случай на стратегическия модел. Ще опишем неговата цел и кога всъщност трябва да обмислим използването му.

Както обикновено, ще предоставим и прост пример за код.

2. Шаблон за нулев обект

В повечето обектно-ориентирани езици за програмиране не ни е позволено да използваме нулева препратка. Ето защо често сме принудени да пишем нулеви проверки:

Command cmd = getCommand(); if (cmd != null) { cmd.execute(); }

Понякога, ако броят на такива, ако изразите станат високи, кодът може да стане грозен, труден за четене и склонен към грешки. Това е моментът, в който Null Object Pattern може да бъде полезен.

Целта на Null Object Pattern е да минимизира този вид нулева проверка. Вместо това можем да идентифицираме нулевото поведение и да го капсулираме в типа, очакван от клиентския код. По-често тогава не, такава неутрална логика е много проста - не правете нищо. По този начин вече не е необходимо да се занимаваме със специална обработка на нулеви препратки.

Ние просто можем да третираме нулеви обекти по същия начин, по който се отнасяме към всеки друг екземпляр от даден тип, който всъщност съдържа по-сложна бизнес логика. Следователно клиентският код остава по-чист.

Тъй като нулевите обекти не трябва да имат никакво състояние, няма нужда да създавате еднакви копия многократно. По този начин често ще прилагаме нулеви обекти като единични .

3. UML диаграма на Null Object Pattern

Нека да разгледаме модела визуално:

Както виждаме, можем да идентифицираме следните участници:

  • Клиентът изисква екземпляр на AbstractObject
  • AbstractObject определя договора, който клиентът очаква - той може също да съдържа споделена логика за изпълняващите класове
  • RealObject реализира AbstractObject и осигурява реално поведение
  • NullObject реализира AbstractObject и осигурява неутрално поведение

4. Изпълнение

Сега, когато имаме ясна представа за теорията, нека разгледаме един пример.

Представете си, че имаме приложение за рутер на съобщения. Всяко съобщение трябва да има валиден приоритет. Нашата система трябва да насочва съобщения с висок приоритет към SMS шлюз, докато съобщенията със среден приоритет трябва да се насочват към JMS опашка.

От време на време обаче в нашето приложение могат да идват съобщения с „недефиниран“ или празен приоритет . Такива съобщения трябва да се изхвърлят от по-нататъшната обработка.

Първо ще създадем интерфейса на рутера :

public interface Router { void route(Message msg); }

След това нека създадем две реализации на горния интерфейс - този, който отговаря за маршрутизация към SMS шлюз и този, който ще насочи съобщенията към JMS опашката:

public class SmsRouter implements Router { @Override public void route(Message msg) { // implementation details } }
public class JmsRouter implements Router { @Override public void route(Message msg) { // implementation details } }

И накрая, нека приложим нашия нулев обект:

public class NullRouter implements Router { @Override public void route(Message msg) { // do nothing } }

Вече сме готови да съберем всички парчета. Нека да видим как може да изглежда примерният клиентски код:

public class RoutingHandler { public void handle(Iterable messages) { for (Message msg : messages) { Router router = RouterFactory.getRouterForMessage(msg); router.route(msg); } } }

Както виждаме, ние третираме всички обекти на рутера по един и същи начин, без значение каква реализация се връща от RouterFactory. Това ни позволява да поддържаме кода си чист и четим.

5. Кога да използвате Null Object Pattern

Трябва да използваме Null Object Pattern, когато клиент иначе би проверил за null, само за да пропусне изпълнението или да извърши действие по подразбиране. В такива случаи можем да капсулираме неутралната логика в нулев обект и да върнем това на клиента вместо нулевата стойност. По този начин клиентският код вече не трябва да е наясно дали даден екземпляр е нулев или не.

Такъв подход следва общите обектно-ориентирани принципи, като Tell-Don't-Ask.

За да разберем по-добре кога трябва да използваме Null Object Pattern, нека си представим, че трябва да внедрим интерфейса на CustomerDao, дефиниран както следва:

public interface CustomerDao { Collection findByNameAndLastname(String name, String lastname); Customer getById(Long id); }

Повечето от разработчиците биха върнали Collections.emptyList () от findByNameAndLastname (), в случай че никой от клиентите не отговаря на предоставените критерии за търсене. Това е много добър пример за следване на Null Object Pattern.

За разлика от това, get ById () трябва да върне на клиента с дадения идентификатор. Някой, който се обажда на този метод, очаква да получи конкретния обект на клиента. В случай че такъв клиент не съществува, трябва изрично да върнем null, за да сигнализираме, че има нещо нередно с предоставения идентификатор.

Както при всички други модели, трябва да разгледаме конкретния ни случай на употреба, преди да приложим сляпо Null Object Pattern . В противен случай може да неволно да въведем някои грешки в нашия код, които ще бъдат трудни за намиране.

6. Заключение

В тази статия научихме какво представлява Null Object Pattern и кога можем да го използваме. Също така внедрихме прост пример за модела на дизайна.

Както обикновено, всички примерни кодове са достъпни в GitHub.