Шаблоны проектирования. Часть 2.

Димитров Вячеслав Михайлович, старший преподаватель кафедры ИМО, dimitrov@cs.petrsu.ru

Special Case

  1. Замена NULL переменной
  2. Если переменная может принимать NULL, то нужно всегда заботиться о проверках
  3. Иногда полезно заменить NULL на объект с тем же интерфейсом но который ведет себя по другому.

Special Case. Пример.


public class Car {
    private bookCustomer = NULL;
    // ...
}

public class Customer {
    // ...
    abstrac public String getName();
}


Special Case. Пример.


public class Car {
    private bookCustomer = new MissingCustomer();
    // ...
}

public class MissingCustomer extends Customer {
    // ...
    public String getName() {
	return "";
    }
}

PubSub

  1. Шаблон передачи сообщений
  2. Отправители (publishers) публикуют сообщения (ничего не знают о подписчиках)
  3. Получатели ничего не знают об отправителя
  4. Сообщения делятся на классы

PubSub. Пример на soket.io. Сервер.


var io = require('socket.io')(http);
io.on('connection', function(socket){
  socket.on('chat_message', function(msg){
    console.log('message: ' + msg);
  });
});

PubSub. Пример на soket.io. Клиент.


var socket = io();
socket.emit('chat_message', 'New message');

LazyLoad.

  1. Объектно-реляционная логика.
  2. Объект, не содержит данных, но знает, где их взять.
  3. Нет необходимости в загрузке данных пока они не потребуются.

LazyLoad. Пример.


// Cars
+----+------+---------------+-----------------+
| Id | Car  | Model         | book_customer_id|
+----+------+---------------+-----------------+
| 18 | car1 | model1        | 79              |
+----+------+---------------+-----------------+

// Customers
+----+------+
| Id | Name |
+----+------+
| 79 | Kate |
+----+------+

LazyLoad. Пример.


public class Car {
    private bookCustomer = null;
    public Car() {
	// ....
	// Load customer.
	this.bookCustomer  = Customer
	    .where('id', this.book_customer_id).first();
    }
    public Customer getBookCustomer() {
	return this.bookCustomer();
    }
    // ...
}

LazyLoad. Пример.


public class Car {
    private bookCustomer = null;
    public Car() {
	// ....
    }
    public Customer getBookCustomer() {
	if (!this.bookCustomer) {
	    // Load customer.
	    this.bookCustomer  = Customer
		.where('id', this.book_customer_id).first();
	}
	return this.bookCustomer();
    }
    // ...
}

Identity Map

  1. Загружать объекты один раз и хранить их в специальных структурах данных (карте присутствия).
  2. При потребности в объекте, сначала ищется в карте.
  3. Если объекта нет, то он загружается и сохраняется в карту.

Identity Map. Пример.


public class Customer extends Model {
    // ...
    abstrac public String getName();
}


Identity Map. Пример.


public class LoadCustomerHelper {
    private Map<id, Model> objects = new HashMap<id, Model>();
    public Model getById(id) {
	if (!this.objects.has(id)) {
	    this.loadById(id);
	}
	return this.objects.get(id);
    }

    public Model loadById(id) {	
	Customer c  = Customer
		.where('id', id).first();
	this.objects.put(id, c);
    }
}


Identity Map. Пример.


public class Car {
    private bookCustomer = null;
    public Car() {
	// ....
    }
    public Customer getBookCustomer() {
	if (!this.bookCustomer) {
	    // Load customer.
	    this.bookCustomer  = LoadCustomerHelper.getInstance().getById(this.book_customer_id);
	}
	return this.bookCustomer();
    }
    // ...
}

Unit of work

  1. Обслуживает набор объектов, изменяемых в бизнес-транзакции.
  2. Каждое изменение объекта записывать в БД - много мелких запросов в БД.
  3. Специальный объект следит за всеми изменениями в объектах
  4. После завершения некоторого действия сохраняет все изменения в БД.

Web. Template View (шаблонизатор).

  1. Заполняет шаблон при помощи маркеров.
  2. Позволяет создавать HTML страницы из шаблона.
  3. Заменяемые данные помечаются маркером.

Web. Template View. Пример.


    <h1>{{event_name}}</h1>
    <span>{{event_date}}</span>
    <div>
        {{event_description}}
    </div>

Web. Template View. Пример.


    $view = View::make('template', [
	'event_name' => 'Name', 
	'event_date' => '29.02.2020', 
	'event_description' => 'Description'
    ]);
    $sHtml = $view->render();

Web. Model View Controller.

  1. Разделяет модель данных, пользовательский интерфейс и управляющую логику.
  2. Модель не зависит от представления и логики.
  3. Модель может иметь несколько представлений.

Web. MVC. Пример. Model.


    public class Model {
	public void extract() {
	    // ...
	}
	public void save() {
	    // ...
	}
    }
    public class Car extends Model {
	public String getDescription() {
	    return this.model + ' ' + this.year;
	}
    }

Web. MVC. Пример. Controller.


    public class CarController {
	public Response cars(Request request) {
	    $aCars = Car::orderBy('created_at')->limit(10);
	    return view('cars', array(
		'aCars' => $aCars,
	    ));
	}
    }

Web. MVC. Пример. View.


    <table>
	<tr>
	    <th>Id</th>
	    <th>Description</th>
	</tr>
    @foreach($aCars as $oCar)
	<tr>
	    <th>{{$oCar->id}}</th>
	    <th>{{$oCar->getDescription()}}</th>
	</tr>
    @endforeach
    </table>

Reactor.

Reactor. Пример NodeJS.

Reactor. Пример NodeJS.


let reloadIniFiles = function () {
    // Read files from file system.
    fs.readdir(configsDir, function (err, files) {
        files.forEach(function (file) {
            if (file.endsWith(".ini")) {
                console.log(file);
                loadIniFile(configsDir + file);
            }
        });
    });
    console.log('after readdir.');
    // Delete service types.
    deleteServiceTypes();
}

Анти-паттерны

Copy-paste

  1. Перенос функции с похожим функционалом из одного проекта в другой, возможно с некоторыми дополнениями.
  2. Ухудшается повторное использование кода
  3. Понижается качество кода
  4. Усложняется поддержка кода

Copy-paste

  1. Code Review значительно усложняется
  2. Оформление повторяющегося кода в виде библиотеки, модуля, пакета, зависимости.

Spaghetti code

  1. Слабо структурированная и плохо спроектированная система, запутанная и очень сложная для понимания.
  2. Подобный код в будущем не сможет разобрать даже его автор.
  3. Небольшое количество объектов.
  4. Огромные по размеру кода методы.

Spaghetti code

  1. Работает - не трогай!
  2. Недостаток опыта разработки.
  3. Решение: рефакторинг или переписать полностью.

Золотой молоток

  1. Уверенность в полной универсальности кода.
  2. Применение одного решения (чаще всего какого-либо одного шаблона проектирования) для всех задач.
  3. Решение: взвешенный выбор в пользу самого удачного решения.

Магические числа

  1. Магическое число — константа, использованная в коде для чего либо
  2. само число не несёт никакого смысла без соответствующего комментария
  3. Числа затрудняют понимание кода и его рефакторинг.
  4. Главными причинами этой ошибки — спешка при разработке.

Hard code

  1. Внедрение различных данных об окружении в реализацию.
  2. Что значит d:\proj\tests.dat?
  3. Непереносимость
  4. Как правило, программист практически сразу забывает где и что он захардкодил

Soft code

  1. Параноидальная боязнь жёсткого кодирования
  2. Конфигурация невероятно сложная и непрозрачная.
  3. Много ресурсов уходит на реализацию возможности настроек абсолютно всего.
  4. Развёртывание такой системы повлечет так же дополнительные затраты.

Ненужная сложность

  1. Заумность решения.
  2. Ненужная сложность может быть внесена в решение любой задачи.
  3. Ненужные проверки.
  4. Усложнению понимания кода.
  5. Понижению скорости работы.

Лодочный якорь

  1. Сохранение неиспользуемых частей системы, которые остались после оптимизации или рефакторинга.
  2. Оставлены «на будущее», авось придётся ещё их использовать
  3. Усложняет систему.

Изобретение велосипеда

  1. Программист разрабатывает собственное решение для задачи, для которой уже существуют решения
  2. Это приводит лишь к потере времени и понижению эффективности работы программиста

Изобретение одноколёсного велосипеда

  1. Создание своего плохого решения, при существовании лучшего.
  2. Тратится на изобретение и реализацию собственного решения.
  3. Тратится при рефакторинге таких решений и замене их оптимальными.

Программирование перебором

  1. А если i+1?
  2. Подбором параметров, порядка вызова функций и так далее
  3. Если программист не понимает происходящего, то он не сможет предусмотреть все варианты развития событий и обязательно о чём-то забудет.

Слепая вера

  1. Недостаточная проверка корректности входных данных, исправления ошибки или результатов работы кода
  2. Но и не следует доводить это недоверие до паранойи

Бездумное комментирование

  1. Большое количество лишних и неинформативных комментариев.
  2. Код не следует комментировать ради комментирования!
  3. Ни в коем случае нельзя допускать диалога разработчиков в комментариях.

Божественный объект

  1. Объект берет на себя слишком много функций и/или хранит в себе практически все данные
  2. Непереносимый код, в котором, к тому же, сложно разобраться.
  3. Вся система зависит практически только от него.

Принцип проектирования и разработки KISS

  1. Keep it simple, stupid
  2. не имеет смысла реализовывать дополнительные функции
  3. не стоит перегружать интерфейс опциями
  4. бессмысленно делать реализацию сложной бизнес-логики, которая учитывает абсолютно все возможные варианты поведения системы

Принцип проектирования и разработки KISS

  1. не имеет смысла беспредельно увеличивать уровень абстракции, надо уметь вовремя остановиться.
  2. бессмысленно закладывать в проект избыточные функции «про запас».
  3. не стоит подключать огромную библиотеку, если вам от неё нужна лишь пара функций.
  4. декомпозиция чего-то сложного на простые составляющие — это архитектурно верный подход.
  5. абсолютная математическая точность или предельная детализация нужны не всегда.

Принцип проектирования и разработки DRY

  1. Don’t repeat yourself
  2. Самый простой подход по уменьшению сложности — разделить систему на управляемые части.
  3. Принцип DRY требует, чтобы такие части информации встречались в вашем коде один, и только один раз.
  4. Каждая часть данных должна иметь четкое, надежное представление в системе.
  5. DRY — это философия, разбивающая логику на представления.

Принцип проектирования и разработки YAGNI

  1. You ain’t gonna need it – вам это не понадобится
  2. KISS старается искать простые решения, а YAGNI просто не делает никаких решений!

Планирование проекта

  1. Достичь меньшей сложности путем уменьшения уровня абстракций
  2. Разделить функционал от возможностей (features)
  3. Учесть небольшие не-функциональные требования
  4. Определить затратные по времени задачи, чтобы избавиться от них