Я считаю, что без ООП и паттернов жизни нет. Тем не менее до сих пор можно встретить множество холиваров различной величины о том насколько вредно или полезно ООП.
В этой небольшом топике я хочу бросить камень в огород противников ООП. Я понимаю, что противником ООП можно быть только в случае если подобная парадигма непонятна, а особенно непонятны причины её использования.
Все резоны в пользу ООП выглядят для начинающего программиста достаточно натянуто, а пробраться сквозь лес интерфейсов и абстрактных классов бывает непросто. Хуже всего то, что действительно объясняющих книг немного. И самая плохая книга которая нереально высоко задирает порог вхождения это самая полезная книга Банды Четырёх о паттернах. Она написана скучным, тоскливым языком с вычурными аккадемическими примерами.
Люди которые ратуют за красивые архитектуры, но при этом объясняют их необходимость на примерах с логерами, авторизациями, кэшем и щедро дают названия классам в виде Foo и Boo делают очень вредное дело. Хотя я и понимаю, что эти люди являются обычно локомативом всего доброго и светлого в программировании. Это очень обидный парадокс.
Хорошо что есть книги такие как Фримен Э. (2 штуки) Паттерны Проектирования. Она послужила и служит истиным озарением для меня при изучении и самое главное практическом использовании паттернов. Я читал книги и посложнее, но не читал полезнее.
Итак, моя история вынужденного использования паттернов.
Изначально, Заказчик сформулировал одну из подзадач примерно так «Мы отправляем документы, написанные на языке клиента, в разные страны. В этих странах свои языки на которых документы могут быть приняты. Необходимо учитывать стоимость перевода и то какой из переводчиков что переводит»
Нет вопросов подумал я и написал первый раз модуль «Переводы» не мудрствуя лукаво и без всяких паттернов
Прошло некоторое время и Заказчик принёс из реального мира следующую новость «Неплохо было бы учитывать тот факт, что брать деньги за один и тот же перевод, который направляется в разные страны — это нехорошо и сильно бросается в глаза»
Я подумал, что это логично и сделал первую прямолинейную оптимизацию модуля «Переводы» по дублированию.
Вскоре Заказчик обнаружил расхождения нашего решения и реальной ситуации. Оказывается в реальном мире переводчик может переводит не напрямую с языка клиента, а перевести вначале на наиболее распространённый язык, например, английский, а потом уже добить всякие экзотические варианты. Это сильно уменьшает общую цену заказа.
Я логично согласился и написал, как мне казалось, максимально сложный алгоритм двойного перевода с оптимизацией по цене.
Если вы думаете, что на этом реальный мир оставил нас в покое, то вы наверное совсем недавно в програмирование.
С небольшим перерывом добавились следующие реальные факты: Документы состоят из частей и некоторые части почти полностью повторяют другие части; У клиента могут быть документы на двух и более языках, один из которых надо «вычитать»; Существуют особые условия в некоторых случаях, например, в некоторых странах документы должны быть переведены на все доступные языки, а в некоторых достаточно одного; Клиент может подождать одного перевода от более дешёвого переводчика и отдать готовый перевод вместе со своими языками... и это ещё не всё, просто я немного устал перечислять.
Если бы вы увидели мой модуль «Переводы» к этому моменту, то основной аргумент противников ООП «работает же без всякого ООП» сильно бы пошатнулся. Алгоритм стал настолько запутанным, что я сам уже с трудом разбирался в нём. И не помогали даже десятки листиков с блок-схемами.
И если вы начинающий программист, как и я, то просто поверьте мне, что мой код был к этому моменту очень похож на рукотворный Ад, хотя и работал.
И в этот момент произошло чудо озарения.
Я долго был не уверен, что инкапсуляция алгоритма допустима при написании алгоритма. Хотя это же очевидно, даже слова похожие, но я не мог поверить, что отдав объектам на откуп их ответственность можно заменить строгую и однозначную логику моих бесконечных foreach и if else. Но я решился и теперь у меня над головой нимб с background image как заставка в Матрице.
И кстати, я давным давно думал, что делиться своими примерами с кем то бесплатно это дикость, а открытый код в конце концов погубит индустрию — это тоже оказалось неправдой. Делиться это здорово. Вот я и делюсь, несколько упростив код.
Для решения моей самой общей задачи я использовал патерны Стратегия и Наблюдатель (у семьи Фриманов они первые в книге, но это совпадение)
Для начала реализуем интерфейс и инкапсулируем стратегии для различных условий перевода в разных странах
public interface ITranslateBehavior
{
bool IsTranslate(StepTranslate stepTranslate, List<languagecomplite> languageList);
}
Вот так выглядит реализаця базового требования «Один из доступных языков
public class BaseTransalte : ITranslateBehavior
{
public bool IsTranslate(StepTranslate stepTranslate, List<languagecomplite> languageList)
{
foreach (LanguageComplite curr in languageList)
{
if (curr.IdLanguage == stepTranslate.IdToLanguage)
{
curr.Iscomplite = true;
curr.Step = stepTranslate;
stepTranslate.Rating++;
return true;
}
}
return false;
}
}
а вот так вариант «Все из доступных»
public class SpecificTransalteRequirement : ITranslateBehavior
{
public bool IsTranslate(StepTranslate stepTranslate, List<languagecomplite> languageList)
{
foreach (LanguageComplite curr in languageList)
{
if (curr.IdLanguage == stepTranslate.IdToLanguage)
{
curr.Iscomplite = true;
curr.Step = stepTranslate;
stepTranslate.Rating++;
}
}
return (!languageList.Where(x => x.Iscomplite == false).Any());
}
}
Вариантов выполнения перевода в зависимости от условий может быть сколько угодно. И теперь для их реализации не потребуется переписывать весь алгоритм. Алгоритма вообще почти не осталось. Нужно будет просто написать новый небольшой класс с новыми требованиями.
StepTranslate — это определённый этап перевода, а List<languagecomplite> — это набор языков с результатами применения этого шага перевода.
public class StepTranslate
{
public int IdFromLanguage { get; set; }
public int IdToLanguage { get; set; }
public decimal Price { get; set; }
public int Step { get; set; }
public bool BeUsed { get; set; }
public int Rating { get; set; }
}
public class LanguageComplite
{
public int IdLanguage { get; set; }
public bool Iscomplite { get; set; }
public StepTranslate Step { get; set; }
}
Теперь со стратегией перевода можно определятся просто добавляя различные новые классы самых неожиданных желаний реального мира.
Для оповещения частей документа о том что наш переводчик перевёл какую то пару языков будем использовать паттерн Наблюдатель.
Определяем интерфейс для переводчика
public interface ITranslator
{
void RegisterParagraph(Paragraf p);
void RemoveParagraf(Paragraf p);
void TranslationParagrafs();
}
Paragraf — Это наша часть документа, которая реализуется вот так
public class Paragraf
{
public int IdCountry { get; set; }
public int IdTypepart { get; set; }
public ITranslateBehavior translateBehavior;
public List<languagecomplite> Languages { get; set; }
public bool transaltedValue = false;
public Translator translator;
public Paragraf(Translator translator)
{
this.translator = translator;
//Это подписка на переводы
this.translator.RegisterParagraph(this);
}
public bool PerformIsTranslate(StepTranslate stepTransalte)
{
//Это реализация паттерна Стратегия
return translateBehavior.IsTranslate(stepTransalte, Languages);
}
}
В моём виртуальном реальном мире Paragraf объявлен как abstract для того чтобы если в мир переводов каким нибудь образом проникнут другие зависимости поведения я мог бы на них отреагировать. Здесь для простоты я реализую его непосредственно.
И собственно сам переводчик
public class Translator : ITranslator
{
private List<paragraf> listParagraf;
private List<steptranslate> stepTranslate;
public Translator(List<steptranslate> stepTranslate)
{
listParagraf = new List<paragraf>();
this.stepTranslate = stepTranslate;
}
public void RegisterParagraph(Paragraf p)
{
listParagraf.Add(p);
}
public void RemoveParagraf(Paragraf p)
{
listParagraf.Remove(p);
}
public void TranslationParagrafs()
{
foreach (StepTranslate currentStep in this.stepTranslate.OrderBy(x => x.Step).ThenBy(y => y.Price))
{
foreach (Paragraf currentParagraf in this.listParagraf.Where(x => !x.transaltedValue))
{
currentParagraf.transaltedValue = currentParagraf.PerformIsTranslate(currentStep);
}
}
}
}
Картинки никак чтоли?
Я несколько упростил код для демонстрации. Убрал оптимизацию по цене и абстракции, и в реальном приложении всё немного сложнее, но в целом достигнутый результат меня радует — код стал понятен любому постороннему человеку, а самое главное он стал понятен мне, и он стал примерно в 50 раз короче.
Возможно прямой алгоритм, без объектов, работал бы быстрее, но возможность потенциальной ошибки в нём, которую практически стало невозможно найти, сводила на нет всё небольшое преимущество в скорости.
Резюме — есть ситуации когда для написания корректно работающих и легко поддерживаемых программ ООП является единственным выходом. И это почти все ситуации в реальном программировании приложений для реального мира.
Спасибо за внмание.
зы Формализую условное ТЗ
Необходимо составить алгоритм двухступенчатого перевода частей документа на языки (языки стран), допустимые в заданном списке стран с учётом минимальной стоимости всего пакета переводов.
Первоначально документ и все его части могут быть предоставлены на нескольких языках — языках клиента.
Условия при которых считается что документ может быть отправлен в страну могут быть вида— В стране есть несколько языков и достаточно одного из них;
— В стране несколько языков и все они обязательные;
— Для некоторых частей документа некоторое подмножество языков страны обязательное;
— Для некоторых частей документа достаточным является хотя бы один из языков из подмножества языков страны;
Условия могут менятся. Например, могут появится условия типа— Для некоторых частей документа достаточным является первод не менее чем на два языка из специального подмножества языков страны
зы Первоначальный заголовок вызвавший неоднозначную реакцию «История о том как я просил 1200, а меня взяли на 2700*»*заголовок для привлечения внимания

Лучшие комментарии пропустить
Куда котиться ДОУ?
www.ibm.com/...ft10/index.html
www.ibm.com/...ft11/index.html
Просто Ваш путь программиста, лежит через практику своих же ошибок, по хорошему нужно учится еще и на чужих и разбирать альтернативные точки зрения и применять те или иные решения в зависимости от ситуации. Есть очень много прикладных областей, для которых паттерны лишняя надстройка, есть где без них никак не обойтись.
Вот как вы определяете почти все? Вы что работали во всез сферах прикладного ПО. Я могу привести с десяток областей, где паттерны будут не нужны и прекрасно укладывают в рамках парадигмы функционального программирования. Так категорично говорить нельзя, ищите дзен...