Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Архитектура информационных систем. Учебное пособие.pdf
Скачиваний:
0
Добавлен:
12.08.2026
Размер:
676 Кб
Скачать

лями функционала системы. В силу высокой сложности современных ИС внесение любого, даже, на первый взгляд, незначительного измене-

ния, потенциально может привести к непредсказуемому поведению системы. В связи с этим принцип открытости/закрытости предлагает не изменять уже существующие и использующиеся программные сущно-

сти, а создавать новые, пользуясь в первую очередь наследованием.

Использование принципа открытости / закрытости также позволя-

ет при необходимости без изменения программного кода возвращать ранее использовавшийся в системе функционал в случае, если новый не отвечает поставленным требованиям. Для достижения такого результа-

та в настоящее время используется технология «внедрения зависимо-

стей» (Dependency Injection) [32].

3.2.3.Принцип подстановки Б. Лисков

В1988 году Барбара Лисков написала следующие строки с форму-

лировкой определения подтипов [25]: «если для каждого объекта o1 ти-

па S существует такой объект o2 типа T, что для всех программ P, оп-

ределённых в терминах T, поведение P не изменяется при подстановке o1 вместо o2, то S является подтипом T1».

Другими словами, программный код должен быть реализован та-

ким образом, чтобы методы (подпрограммы P) могли работать с клас-

сами-наследниками точно так же (т.е. без необходимости изменения программного кода), как они работают с классами-родителями.

В качестве примера ниже рассмотрены два фрагмента программ-

ного кода. В первом фрагменте третий принцип SOLID не соблюдается

(см. листинг 1).

Листинг 1 – Пример несоблюдения принципа LSP

function AnimalLegCount(animals: Array<Animal>) {

32

let legCount = 0;

for(let i = 0; i <= animals.length; i++) { let animal = animals[i];

if (typeof(animal) == "Lion") legCount += LionLegCount(animal);

else if (typeof(animal) == "Mouse") legCount += MouseLegCount(animal);

else if (typeof(animal) == "Snake") legCount += SnakeLegCount(animal);

}

return legCount;

}

Метод AnimalLegCount принимает на вход массив животных и подсчитывает количество их ног. Внутри метода делается три предпо-

ложения относительно типа животного, и в зависимости от реального типа данных вызывается подходящая для получения числа ног подпро-

грамма. Проблема в коде листинга 1 заключается в том, что при необ-

ходимости подсчитать количество ног, например, у страуса потребуется вносить изменения в программный код, что потенциально приведёт к нарушению метода открытости/закрытости.

Во втором фрагменте третий принцип SOLID соблюдается (см.

листинг 2).

Листинг 2 – Измененная функция подсчета количества конечно-

стей

function AnimalLegCount(animals: Array<Animal>) { let legCount = 0;

for(let i = 0; i <= a.length; i++) { legCount += animals[i].LegCount();

}

return legCount;

33

}

abstract class Animal {

//прочие поля и поведение программной сущности abstarct function LegCount();

}

class Lion extends Animal{

//прочие поля и поведение программной сущности override function LegCount() {

//получение количества ног у льва

}

}

//аналогично Lion описываются классы Mouse, Snake

Новая функция не пытается узнать конкретный тип объекта, она обрабатывает каждый объект как подкласс основного класса Animal. В

результате обращения вызывается метод вычисления количества ног,

реализованный в каждом из подклассов. Теперь при добавлении нового класса «Страус» метод AnimalLegCount изменять не потребуется, что повысит стабильность работы разрабатываемой ИС.

Принцип подстановки Барбары Лисков может и должен распро-

страняться до уровня архитектуры. Простое нарушение совместимости может вызвать загрязнение архитектуры системы значительным коли-

чеством дополнительных механизмов обработки объектов.

Инициализация структур данных (в рассмотренном примере – мас-

сив Animal) при соблюдении принципа подстановки Б. Лисков будет осуществляться посредством шаблона проектирования «Абстрактная фабрика» (см. раздел 3.6) и шаблона внедрения зависимостей.

3.2.4. Принцип разделения интерфейсов

Происхождение названия принципа разделения интерфейсов на-

глядно иллюстрирует схема на рис. 3.

34

Рисунок 3 – Диаграмма классов, не соответствующая принципу разделения интерфейсов

На рис. 3 представлено несколько программных сущностей, поль-

зующихся операциями в классе OPS. Пусть класс User1 использует только операцию op1, User2 – только op2 и User3 – только op3. В такой ситуации исходный код User1 непреднамеренно будет зависеть от op2 и op3, даже с учётом того, что не использует (не реализует) их. Эта зави-

симость означает, что изменения в исходном коде метода op2 в классе

OPS потребуют повторной компиляции и развёртывания класса User1,

несмотря на то, что для него ничего не изменилось. Эту проблему мож-

но решить разделением операций по интерфейсам, как показано на рис. 4.

35

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]