В этой статье речь пойдет о наборе шаблонов ООП, использующих простую композицию, а не наследование.
Большинство шаблонов взяты из книги Gang of Four: Design Patterns. Мы рассмотрим лишь краткое введение в каждый из них, для более подробного ознакомления перейдите по ссылке.
Фабрика — это объект, единственной задачей которого является создание других объектов. Их можно создавать разными способами, однако шаблон абстрактной фабрики — наиболее эффективный вариант.
С помощью этого шаблона можно не только изменять сгенерированный или созданный объект во время выполнения, но и саму фабрику. Он также отлично работает с фреймворками, использующими inversion of control, такими как Spring или Unity.
Код выглядит следующим образом:
interface Factory<T> {
T build(Metadata d)
}
class ClientFactory implements Factory<Client> {
Client build(Metadata d) {
// Build actual object and return
}
}
Абстрактные фабрики удобны в использовании, если нужно создать объект, соответствующий простому интерфейсу на основе конфигурации, так, чтобы все остальные классы, использующие этот объект, не знали об изменениях. Основные идеи — это те же классические идеи с другими принципами разработки ПО: скрытие информации, классы, выполняющие одно действие, и небольшие интерфейсы.
Многие разработчики при работе над определенным проектом (кодом) делегируют работу другим членам команды вместо того, чтобы выполнять ее самостоятельно.
Шаблон делегирования работает по тому же принципу: классы высшего порядка просят классы низшего порядка выполнить работу за них, оставаясь при этом более простыми и зная меньше информации о находящихся ниже структурах.
Код выглядит следующим образом:
interface Validator {
bool validate(Object o)
}
class ValidatorHelper implements Validator {
Set<Validator> delegates;
bool validate(Object o) {
for (Validator v : delegates) {
if (!v.validate(o)) return false
}
return true
}
}
class RestController {
ValidationHelper helper;
Response addObject(Object o) {
if (helper.validate(o)) return ErrorResponse
// Normal processing
}
}
Использование делегатов полезно при работе с валидацией, сортировкой, нормализацией и т. д.
С помощью шаблона строитель можно создать гибкий и расширяемый код без особых усилий и при желании использовать возможности неизменяемости!
Другие языки могут не иметь шаблон строитель (или даже не нуждаться в нем), благодаря наличию именованных параметров в конструкторах со стандартными значениями по умолчанию. Они предоставляют те же возможности: объявление только тех элементов, для которых нужно установить определенное значение.
Код выглядит следующим образом:
class Dto {
String s
int i
private Dto(String s, int i) {
this.s = s
this.i = i
}
public DtoBuilder builder() {
return new DtoBuilder()
}
public static class DtoBuilder {
private String s = "some string"
private int i = 0
public DtoBuilder withString(String s) {
this.s = s
return this
}
public DtoBuilder withInt(int it) {
this.i = i
return this
}
public Dto build() {
return new Dto(s, i)
}
}
}
Примечание: в Java также используется Lombok для выполнения утомительного процесса написания кода.
Причина, по которой этот шаблон так упрощает код, заключается в том, что когда все объекты используют конструктор, создание нового происходит автоматически. Не нужно тратить время на просмотр конструкторов и кода строителя.
Этот шаблон тесно связан с цепочкой ответственности и шаблонным методом. В нем каждая «цепочка» расширяет или дополняет объект, а затем возвращает его. Эти действия можно выполнить с каждым расширителем в цепочке, или же цепочка может пропустить остальную часть при необходимости.
Кажется, что это нарушает правила чистого кода о побочных эффектах в функции. Однако это не так, поскольку расширитель должен возвращать расширенный объект вызывающему объекту, заявляя, что объект может измениться. Насколько известно вызывающему объекту, это может быть новый объект (особенно в случае неизменяемых ограничений).
interface Enricher<T> {
T enrich(T thing);
}
class HeadersEnricher implements Enricher<Headers> {
Headers enrich(Headers headers) {
headers.add("x-header", "something")
return headers
}
}
Этот шаблон отлично подходит для наполнения объекта новым состоянием. Например, если у вас есть объект, поступающий из потока Kafka, к которому необходимо добавить данные, прежде чем сохранить в хранилище данных.
Перевод статьи Dan Goslen: My Top 4 Patterns for Writing Simple Code
Комментарии