Patterns & Algorythms/ Flashcards
Что такое «шаблон проектирования»?
Проверенное и готовое к использованию архитектурное решение не привязанное к конкретному языку.
Плюсы:
* упрощение разработки за счет готовых абстракций;
* облегчение коммуникации между разработчиками.
Минусы:
* слепое следование некоторому шаблону может привести к усложнению программы;
* желание попробовать некоторый шаблон в деле без особых на то оснований.
Назовите основные характеристики шаблонов.
- имя – все шаблоны имеют уникальное имя, служащее для их идентификации;
- назначение данного шаблона;
- задача, которую шаблон позволяет решить;
-
способ решения, предлагаемый в шаблоне для решения задачи в том контексте, где
этот шаблон был найден; - участники – сущности, принимающие участие в решении задачи;
-
следствия от использования шаблона как результат действий, выполняемых в
шаблоне; - реализация – возможный вариант реализации шаблона.
Назовите три основные группы паттернов.
Порождающие – абстрагируют процесс создание экземпляра, делегируя этот процесс другому объекту.
Они позволяют сделать систему независимой от способа создания и композиции объектов и избежать внесения в программу лишних зависимостей.
Структурные – отвечают за построение удобной в поддержке системы зависимостей между объектами с целью создания более функциональных структур.
Поведенческие – заботятся об эффективной коммуникации между объектами.
Основные – основные строительные блоки, используемые для построения других
шаблонов. Например, интерфейс.
Расскажите про паттерн Одиночка (Singleton).
Порождающий паттерн проектирования, который гарантирует, что у класса есть только один
экземпляр, и предоставляет к нему единую точку доступа.
Конструктор помечается как private, а для создания нового объекта Singleton использует
специальный метод getInstance(). Он либо создает объект, либо отдаёт существующий
объект, если он уже был создан.
Чтобы обеспечить потокобезовасность его лучше создавать при поднятии контекста
private static final Singleton instance = new Singleton();
____________________
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
Плюсы:
* можно не создавать множество объектов для ресурсоемких задач, а пользоваться
одним.
Минусы:
- нарушает принцип единой ответственности, так как
1) несет функционал
2) Гарантирует наличие одной сущности
3) обеспечивает единую точку доступа к этой сущности - не мокается в тестах
- если он изменяем то изменения отразятся на всех использующих его объектах
- не работают в распределенных системах и при многопоточке.
Расскажите про паттерн Прототип (Prototype).
Порождающий паттерн проектирования, который позволяет копировать объекты, не вдаваясь в подробности их реализации.
Паттерн поручает создание копий самим копируемым объектам. Он вводит общий интерфейс с методом clone для всех объектов, поддерживающих клонирование. Реализация этого метода в разных классах очень схожа.
Метод создаёт новый объект текущего класса и копирует в него значения всех полей собственного объекта.
+ : Позволяет клонировать объекты, не привязываясь к их конкретным классам.
- : Сложно клонировать составные объекты, имеющие ссылки на другие объекты.
public class Person implements CloneablePrototype<Person> { private String name; private int age; private String address; public Person(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } public String getName() { return name; } public int getAge() { return age; } public String getAddress() { return address } public void setAddress(String address) { this.address = address; } @Override public Person clonePrototype() { try { Person cloned = (Person) super.clone(); cloned.name = this.name; cloned.age = this.age; cloned.address = new String(this.address); return cloned; } catch (CloneNotSupportedException e) { throw new AssertionError(); } } }
Расскажите про паттерн Строитель (Builder).
Порождающий паттерн, который позволяет создавать сложные объекты пошагово.
Паттерн предлагает вынести конструирование объекта за пределы его собственного класса,
(как правило) поручив это дело вложенному статическому классу.
+ упрощает создание сложных объектов, делая код создания более читаемым и предоставляет единый механизм создания независимо от количества аргументов.
- необходимость создания отдельного класса и увеличение общего количества строк.
- проверка всех полей перед
public class Car { private String make; private String model; private int year; private int mileage; private Car(Builder builder) { this.make = builder.make; this.model = builder.model; this.year = builder.year; this.mileage = builder.mileage; } public static class Builder { private String make; private String model; private int year; private int mileage; public Builder setMake(String make) { this.make = make; return this; } public Builder setModel(String model) { this.model = model; return this; } public Builder setYear(int year) { this.year = year; return this; } public Builder setMileage(int mileage) { this.mileage = mileage; return this; } public Car build() { return new Car(this); } } } Car car = new Car.Builder() .setMake("Toyota") .setModel("Corolla") .setYear(2021) .setMileage(10000) .build();
Расскажите про паттерн Фабричный метод (Factory Method).
Фабричный метод — это порождающий паттерн проектирования, он определяет интерфейс создания объекта, но позволяет субклассам выбрать класс создаваемого экземпляра. Таким образом, Фабричный Метод делегирует операцию создания экземпляра субклассам.
Предполагается наличие двух параллельных иерархий: создатели и продукты.
Родительский создатель содержит фабричный метод abstract Product factoryMethod(String type)
, наследники его реализуют и возвращают экземпляр продукта-родителя.
То есть клиент создает конкретную имплементацию фабрики, а не продукт и к ней обращается за продуктом.
Таким образом мы соблюдаем SOLID.
+ : инкапсулирует создание объектов, предлагая единый интерфейс для этого процесса и тип возвращаемого объекта может варьироваться в зависимости от входных параметров.
- : не масштабируются для большого кол-ва необязательных параметров.
Зачем фабрики оформляются в виде статических методов?
Чтобы метод create можно было вызывать и без создания экземпляра объекта. С другой стороны,
теряется возможность субклассирования и изменения поведения метода create.
Пример: SessionFactory в Hibernate.
public abstract class PizzaStore { abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--- Making a " + pizza.getName() + " ---"); pizza.prepare(); return pizza; }} public class NYPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals("cheese")) { return new NYStyleCheesePizza(); } else if (item.equals("veggie")) { return new NYStyleVeggiePizza(); } else if (item.equals("clam")) { return new NYStyleClamPizza(); } else if (item.equals("pepperoni")) { return new NYStylePepperoniPizza(); } else return null; }} public abstract class Pizza { String name; void prepare() { System.out.println("Prepare " + name); } public class NYStyleCheesePizza extends Pizza { public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza";}} public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese");
Расскажите про паттерн Абстрактная фабрика (Abstract Factory).
Абстрактная фабрика — это порождающий паттерн проектирования, который позволяет создавать семейства связанных объектов, не привязываясь к конкретным классам создаваемых объектов, а используя сущности, реализующие определенные интерфейсы.
java.sql.Connection: сущности Connection создаются с помощью DriverManager, который является абстрактной фабрикой.
public interface MenuFactory { Menu createMenu(); Dish createDish(); } public class BreakfastMenuFactory implements MenuFactory { public Menu createMenu() { return new BreakfastMenu(); } public Dish createDish() { return new BreakfastDish(); } } public class LunchMenuFactory implements MenuFactory { public Menu createMenu() { return new LunchMenu(); } public Dish createDish() { return new LunchDish(); } } public interface Menu { String getName(); List<Dish> getDishes(); } public class BreakfastMenu implements Menu { public String getName() { return "Breakfast menu"; } public List<Dish> getDishes() { // return a list of breakfast dishes } } public class LunchMenu implements Menu { public String getName() { return "Lunch menu"; } public List<Dish> getDishes() { // return a list of lunch dishes } } public interface Dish { String getName(); List<String> getIngredients(); } public class BreakfastDish implements Dish { public String getName() { return "Breakfast dish"; } public List<String> getIngredients() { // return a list of breakfast dish ingredients } } public class LunchDish implements Dish { public String getName() { return "Lunch dish"; } public List<String> getIngredients() { // return a list of lunch dish ingredients } }
MenuFactory breakfastFactory = new BreakfastMenuFactory();
Menu breakfastMenu = breakfastFactory.createMenu();
Dish breakfastDish = breakfastFactory.createDish();
Расскажите про паттерн Адаптер (Adapter).
Адаптер — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе.
1) Адаптер имеет интерфейс, который совместим с первым объектом и содержит второй объект.
2) Поэтому на объекте адаптера можно вызывать методы первого объекта.
3) Адаптер получает эти вызовы и перенаправляет их второму объекту, но уже в том формате и последовательности, которые понятны второму объекту.
public interface Enumeration<E> { boolean hasMoreElements(); E nextElement(); } public interface Iterator<E> { boolean hasNext(); E next(); } public class EnumerationIterator implements Iterator<Object> { Enumeration<?> enumeration; public EnumerationIterator(Enumeration<?> enumeration) { this.enumeration = enumeration; } public boolean hasNext() { return enumeration.hasMoreElements(); } public Object next() { return enumeration.nextElement(); } } public static void main (String args[]) { Vector<String> v = new Vector<String>(Arrays.asList(...)); Iterator<?> iterator = new EnumerationIterator(v.elements()); while (iterator.hasNext()) { System.out.println(iterator.next()); } }
Расскажите про паттерн Декоратор (Decorator).
Декоратор — это структурный паттерн проектирования, который позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».
Класс декоратор и декорируемый класс имплементируют общий интерфейс и декоратор содержит декорируемый объект.
ТАкже есть классы, расширяющие декоратор, они и добавляют свою реализацию методов интерфейса к реализации этого метода декорируемым объектом.
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
public class HouseBlend extends Beverage {
public HouseBlend() {
description = “House Blend Coffee”;
}
public double cost() {
return .89;
}
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + “, Mocha”;
}
public double cost() {
return .20 + beverage.cost();
}
Beverage beverage2 = new HouseBlend();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription()
+ “ $” + beverage2.cost());
Расскажите про паттерн Заместитель (Proxy).
Заместитель — это структурный паттерн проектирования, который позволяет подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-то до или после передачи вызова оригиналу.
В отличии от декоратора клиент не знает, что общается с прокси, он просто обращается к объкту общего для прокси и базового объекта интерфейсу.
interface DataService {
void fetchData();
}
class DataServiceProxy implements DataService {
private final DataService realService;
public DataServiceProxy() { this.realService = new RealDataService(); } @Override public void fetchData() { System.out.println("Fetching data from proxy..."); realService.fetchData(); System.out.println("Data fetched."); } }
class RealDataService implements DataService {
@Override
public void fetchData() {
System.out.println(“Fetching data from real service…”);
}
}
public class Main {
public static void main(String[] args) {
DataService service = new DataServiceProxy();
service.fetchData();
}
}
Расскажите про паттерн Итератор (Iterator).
Итератор — это поведенческий паттерн проектирования, который даёт возможность последовательно обходить элементы составных объектов (коллекции), не раскрывая конкретную реализацию этих объектов.
Объект-итератор будет отслеживать состояние обхода, текущую позицию в коллекции и сколько элементов ещё осталось обойти. Одну и ту же коллекцию смогут одновременно обходить различные итераторы, а сама коллекция не будет даже знать об этом.
А мы получаем единый интерфейс прохода по элементам, независимо от типа структуры данных.
Расскажите про паттерн Шаблонный метод (Template Method).
Шаблонный Метод определяет основные шаги алгоритма и позволяет субклассам предоставить реализацию одного или нескольких шагов:
- алгоритм определяется суперклассом, он же содержит дублирующийся код и лишь делегирует подклассам специфичные для них задачи.
- легко изменить алгоритм, так как он весь в одном месте
- легко добавить новые реализации, переопределив пару методов.
public abstract class Task { private final String name; public Task(String name) { this.name = name; } // Template method public final void execute() { initialize(); doTask(); shutdown(); } protected void initialize() { System.out.println("Initializing " + name + " task..."); } protected abstract void doTask(); protected void shutdown() { System.out.println("Shutting down " + name + " task..."); } } public class EmailTask extends Task { public EmailTask(String name) { super(name); } @Override protected void doTask() { System.out.println("Sending emails for " + getName() + " task..."); } } public class FileTask extends Task { public FileTask(String name) { super(name); } @Override protected void doTask() { System.out.println("Processing files for " + getName() + " task..."); } } public class Main { public static void main(String[] args) { Task emailTask = new EmailTask("Email"); emailTask.execute(); Task fileTask = new FileTask("File"); fileTask.execute(); } }
Расскажите про паттерн Цепочка обязанностей (Chain of Responsibility).
Цепочка обязанностей — это поведенческий паттерн проектирования, который позволяет передавать запросы последовательно по цепочке обработчиков. Каждый последующий обработчик решает, может ли он обработать запрос сам и стоит ли передавать запрос дальше по цепи.
public interface ComplaintHandler { void handleComplaint(String complaint); } public class BillingComplaintHandler implements ComplaintHandler { private ComplaintHandler nextHandler; public void setNextHandler(ComplaintHandler nextHandler) { this.nextHandler = nextHandler; } public void handleComplaint(String complaint) { if (complaint.contains("billing")) { System.out.println("Complaint handled by Billing team."); } else if (nextHandler != null) { nextHandler.handleComplaint(complaint); } else { System.out.println("No team available to handle this complaint."); } } } public class ShippingComplaintHandler implements ComplaintHandler { private ComplaintHandler nextHandler; public void setNextHandler(ComplaintHandler nextHandler) { this.nextHandler = nextHandler; } public void handleComplaint(String complaint) { if (complaint.contains("shipping")) { System.out.println("Complaint handled by Shipping team."); } else if (nextHandler != null) { nextHandler.handleComplaint(complaint); } else { System.out.println("No team available to handle this complaint."); } } } public class GeneralComplaintHandler implements ComplaintHandler { public void handleComplaint(String complaint) { System.out.println("Complaint handled by General team."); } } public static void main(String[] args) { ComplaintHandler billingHandler = new BillingComplaintHandler(); ComplaintHandler shippingHandler = new ShippingComplaintHandler(); ComplaintHandler generalHandler = new GeneralComplaintHandler(); billingHandler.setNextHandler(shippingHandler); shippingHandler.setNextHandler(generalHandler); billingHandler.handleComplaint("My billing statement is incorrect.");
Расскажите про шаблон Наблюдатель
Определяет отношение «один-ко-многим» между объектами таким образом,
что при изменении состояния одного объекта (субъект) происходит автоматическое оповещение и обновление всех зависимых объектов (наблюдатели).
Субъект содержит список зарегистрированных наблюдателей и при изменении своего состояния проходит по списку и вызывает у них метод update().
public interface Observer { public void update(int value); } public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); } public class SimpleObserver implements Observer { private int value; private Subject simpleSubject; public SimpleObserver(Subject simpleSubject) { this.simpleSubject = simpleSubject; simpleSubject.registerObserver(this); } public void update(int value) { this.value = value; display(); } public void display() { System.out.println("Value: " + value); }} public class SimpleSubject implements Subject { private List<Observer> observers; private int value = 0; public SimpleSubject() { observers = new ArrayList<Observer>(); } public void registerObserver(Observer o) { observers.add(o); } public void removeObserver(Observer o) { observers.remove(o); } public void notifyObservers() { for (Observer observer : observers) { observer.update(value); } } public void setValue(int value) { this.value = value; notifyObservers(); }} public static void main(String[] args) { SimpleSubject simpleSubject = new SimpleSubject(); SimpleObserver simpleObserver = new SimpleObserver(simpleSubject); simpleSubject.setValue(80); simpleSubject.removeObserver(simpleObserver); }