У меня есть веб-приложение. Я не верю, что технология важна. Структура представляет собой N-уровневое приложение, показанное на рисунке слева. Есть 3 слоя.
UI (шаблон MVC), уровень бизнес-логики (BLL) и уровень доступа к данным (DAL)
Проблема, которую я имею, состоит в том, что мой BLL огромен, поскольку в нем есть логика и пути для вызова событий приложения.
Типичный поток через приложение может быть:
Событие, генерируемое в пользовательском интерфейсе, переходит к методу в BLL, выполняет логику (возможно, в нескольких частях BLL), в конечном итоге к DAL, обратно к BLL (где, вероятно, больше логики), а затем возвращает некоторое значение в UI.
BLL в этом примере очень занят, и я думаю, как это разделить. У меня также есть логика и объединенные объекты, которые мне не нравятся.
Версия справа - мое усилие.
Логика по-прежнему заключается в том, как приложение перемещается между пользовательским интерфейсом и DAL, но, скорее всего, свойств нет ... Только методы (большинство классов на этом уровне могут быть статическими, поскольку они не хранят никакого состояния). Слой Poco - это место, где существуют классы, которые имеют свойства (например, класс Person, в котором есть имя, возраст, рост и т. Д.). Они не имеют ничего общего с потоком приложения, они только хранят состояние.
Поток может быть:
Даже запускается из пользовательского интерфейса и передает некоторые данные в контроллер уровня пользовательского интерфейса (MVC). Это преобразует необработанные данные и преобразует их в модель Poco. Затем модель poco передается на уровень логики (который был BLL) и, в конце концов, на уровень командных запросов, которые потенциально могут быть обработаны в пути. Уровень командных запросов преобразует POCO в объект базы данных (это почти одно и то же, но один предназначен для персистентности, другой - для внешнего интерфейса). Элемент сохраняется, и объект базы данных возвращается на уровень командного запроса. Затем он преобразуется в POCO, где он возвращается на уровень логики, потенциально обрабатывается дальше и, наконец, возвращается к пользовательскому интерфейсу.
Общая логика и интерфейсы - это место, где у нас могут быть постоянные данные, такие как MaxNumberOf_X и TotalAllowed_X и все интерфейсы.
И общая логика / интерфейсы, и DAL являются «основой» архитектуры. Они ничего не знают о внешнем мире.
Все знают о poco кроме общей логики / интерфейсов и DAL.
Поток все еще очень похож на первый пример, но он делает каждый слой более ответственным за одну вещь (будь то состояние, поток или что-то еще) ... но я нарушаю ООП с этим подходом?
Примером демонстрации Logic и Poco может быть:
public class LogicClass
{
private ICommandQueryObject cmdQuery;
public PocoA Method1(PocoB pocoB)
{
return cmdQuery.Save(pocoB);
}
/*This has no state objects, only ways to communicate with other
layers such as the cmdQuery. Everything else is just function
calls to allow flow via the program */
public PocoA Method2(PocoB pocoB)
{
pocoB.UpdateState("world");
return Method1(pocoB);
}
}
public struct PocoX
{
public string DataA {get;set;}
public int DataB {get;set;}
public int DataC {get;set;}
/*This simply returns something that is part of this class.
Everything is self-contained to this class. It doesn't call
trying to directly communicate with databases etc*/
public int GetValue()
{
return DataB * DataC;
}
/*This simply sets something that is part of this class.
Everything is self-contained to this class.
It doesn't call trying to directly communicate with databases etc*/
public void UpdateState(string input)
{
DataA += input;
}
}
источник
Ответы:
Да, вы, скорее всего, нарушаете основные концепции ООП. Однако не чувствуйте себя плохо, люди делают это все время, это не значит, что ваша архитектура «неправильна». Я бы сказал, что это, вероятно, менее ремонтопригодно, чем правильный дизайн ОО, но это скорее субъективно и не ваш вопрос в любом случае. ( Вот моя статья с критикой n-уровневой архитектуры в целом).
Обоснование . Самая основная концепция ООП состоит в том, что данные и логика образуют единое целое (объект). Хотя это очень упрощенное и механическое утверждение, тем не менее, оно не соответствует действительности в вашем дизайне (если я вас правильно понимаю). Вы довольно четко отделяете большую часть данных от большей части логики. Например, наличие методов без сохранения состояния (статических) называется «процедурами» и, как правило, противоречит ООП.
Конечно, всегда есть исключения, но этот дизайн, как правило, нарушает эти вещи.
Опять же, я хотел бы подчеркнуть, что «нарушает ООП»! = «Неправильно», так что это не обязательно является оценочным суждением. Все зависит от ваших архитектурных ограничений, сценариев использования, требований и т. Д.
источник
Один из основных принципов функционального программирования - это чистые функции.
Одним из основных принципов объектно-ориентированного программирования является объединение функций с данными, на которые они действуют.
Оба эти основных принципа отпадают, когда ваше приложение должно взаимодействовать с внешним миром. На самом деле вы можете быть верны этим идеалам только в специально подготовленном пространстве вашей системы. Не каждая строка вашего кода должна соответствовать этим идеалам. Но если ни одна строка вашего кода не отвечает этим идеалам, вы не можете утверждать, что используете ООП или FP.
Так что нормально иметь только «объекты» данных, которые вы бросаете, потому что они нужны вам, чтобы пересечь границу, которую вы просто не можете реорганизовать, чтобы переместить интересующий код. Просто знайте, что это не ООП. Это реальность ООП - это когда однажды внутри этой границы вы собираете всю логику, которая воздействует на эти данные, в одно место.
Не то чтобы ты тоже это делал. ООП не все для всех людей. Что есть, то есть. Только не утверждайте, что что-то следует за ООП, когда это не так, иначе вы запутаете людей, пытающихся поддерживать ваш код.
В вашем POCO, кажется, есть бизнес-логика, и я не буду слишком беспокоиться о анемии. Что меня беспокоит, так это то, что все они кажутся очень изменчивыми. Помните, что геттеры и сеттеры не обеспечивают реальной инкапсуляции. Если ваш POCO движется к этой границе, тогда хорошо. Просто поймите, что это не дает вам всех преимуществ реального инкапсулированного объекта ООП. Некоторые называют это объектом передачи данных или DTO.
Уловка, которую я успешно использовал, заключается в создании объектов ООП, которые питаются DTO. Я использую DTO в качестве объекта параметра . Мой конструктор читает из него состояние (читается как защитная копия ) и отбрасывает его в сторону. Теперь у меня есть полностью инкапсулированная и неизменная версия DTO. Все методы, связанные с этими данными, могут быть перемещены сюда, если они находятся на этой стороне этой границы.
Я не предоставляю добытчики или сеттеры. Я следую, скажи, не спрашивай . Вы называете мои методы, и они делают то, что нужно. Скорее всего, они даже не скажут вам, что они сделали. Они просто делают это.
Теперь, в конце концов, кое-что, где-нибудь, столкнется с другой границей, и все это снова развалится. Отлично. Раскрути еще один DTO и брось его через стену.
В этом суть архитектуры портов и адаптеров. Я читал об этом с функциональной точки зрения . Может быть, вас это тоже заинтересует.
источник
Если я правильно прочитал ваше объяснение, ваши объекты выглядят примерно так: (сложно без контекста)
Так как ваши классы Poco содержат только данные, а ваши классы логики содержат методы, которые воздействуют на эти данные; да, вы нарушили принципы "Классик ООП"
Опять же, по вашему обобщенному описанию трудно сказать, но я бы рискнул, чтобы то, что вы написали, могло быть классифицировано как Anemic Domain Model.
Я не думаю, что это особенно плохой подход, и, если вы считаете, что ваш Poco как структура, он не нарушает ООП в более конкретном смысле. В этом ваши объекты теперь являются классами логики. В самом деле, если вы сделаете ваш Pocos неизменным, дизайн можно считать достаточно функциональным.
Однако, когда вы ссылаетесь на Shared Logic, Pocos, которые почти одинаковы, и на статику, я начинаю беспокоиться о деталях вашего дизайна.
источник
Одна потенциальная проблема, которую я видел в вашем дизайне (и она очень распространена) - некоторые из самых худших «ОО» -кодов, с которыми я когда-либо сталкивался, были вызваны архитектурой, которая отделяла объекты «Данные» от объектов «Код». Это вещи уровня кошмара! Проблема в том, что везде в вашем бизнес-коде, когда вы хотите получить доступ к своим объектам данных, вы склоняетесь к тому, чтобы просто кодировать их прямо в строке (вам не нужно, вы можете создать служебный класс или другую функцию для его обработки, но это то, что Я видел, как это происходило неоднократно с течением времени).
Код доступа / обновления, как правило, не собирается, поэтому у вас везде есть дублирующиеся функции.
С другой стороны, эти объекты данных полезны, например, для сохранения базы данных. Я пробовал три решения:
Копирование значений в и из «реальных» объектов и отбрасывание вашего объекта данных утомительно (но может быть правильным решением, если вы хотите пойти по этому пути).
Добавление методов обработки данных к объектам данных может работать, но это может привести к большому беспорядочному объекту данных, который делает больше чем одно. Это также может усложнить инкапсуляцию, поскольку многие механизмы персистентности хотят иметь общедоступные средства доступа ... Мне не понравилось, когда я это сделал, но это правильное решение
Лучшее решение для меня - это концепция класса Wrapper, который инкапсулирует класс «Data» и содержит все функции обработки данных - тогда я вообще не раскрываю класс данных (даже сеттеры и геттеры) если они абсолютно не нужны). Это устраняет искушение напрямую манипулировать объектом и вынуждает вас добавлять общие функции к оболочке.
Другое преимущество заключается в том, что вы можете убедиться, что ваш класс данных всегда находится в допустимом состоянии. Вот быстрый пример psuedocode:
Обратите внимание, что у вас нет проверки возраста, разбросанной по всему коду в разных областях, а также что у вас нет соблазна использовать его, потому что вы даже не можете понять, какой день рождения (если вам это не нужно для чего-то другого, в в каком случае вы можете добавить его).
Я склонен не просто расширять объект данных, потому что вы теряете эту инкапсуляцию и гарантию безопасности - в этот момент вы могли бы просто добавить методы в класс данных.
Таким образом, в вашей бизнес-логике не будет разбросано множество ненужных / итераторов для доступа к данным, она станет намного более читабельной и менее избыточной. Я также рекомендую приобретать привычку всегда упаковывать коллекции по одной и той же причине - не допускать циклических / поисковых конструкций в своей бизнес-логике и следить за тем, чтобы они всегда были в хорошем состоянии.
источник
Никогда не меняйте свой код, потому что вы думаете, или кто-то говорит вам, что это не то или не так. Измените свой код, если он создает проблемы, и вы нашли способ избежать этих проблем, не создавая другие.
Таким образом, помимо того, что вам не нравятся вещи, вы хотите потратить много времени, чтобы внести изменения. Запишите проблемы, которые у вас есть сейчас. Запишите, как ваш новый дизайн решит проблемы. Выясните ценность улучшения и стоимость внесения ваших изменений. Затем - и это наиболее важно - убедитесь, что у вас есть время для завершения этих изменений, иначе вы окажетесь наполовину в этом состоянии, наполовину в этом состоянии, и это наихудшая возможная ситуация. (Однажды я работал над проектом с 13 различными типами строк и тремя опознаваемыми наполовину попытками стандартизировать один тип)
источник
Категория «ООП» намного больше и более абстрактна, чем вы описываете. Это не заботится обо всем этом. Он заботится о четкой ответственности, сплоченности, сцеплении. Таким образом, на уровне, который вы спрашиваете, не имеет смысла спрашивать о «OOPS Practice».
Тем не менее, к вашему примеру:
Мне кажется, что есть недоразумение о том, что означает MVC. Вы называете свой пользовательский интерфейс «MVC» отдельно от бизнес-логики и «внутреннего» управления. Но для меня MVC включает в себя целое веб-приложение:
Здесь есть несколько чрезвычайно важных базовых допущений:
Важно: пользовательский интерфейс является частью MVC. Не наоборот (как на вашей диаграмме). Если принять это, то толстые модели на самом деле довольно хороши - при условии, что они действительно не содержат того, чего не должны.
Обратите внимание, что «толстые модели» означают, что вся бизнес-логика находится в категории «Модель» (пакет, модуль, независимо от названия на выбранном вами языке). Очевидно, что отдельные классы должны быть хорошо структурированы по ООП в соответствии с любыми указаниями по кодированию, которые вы сами себе даете (т. Е. Некоторые максимальные строки кода на класс или метод и т. Д.).
Также обратите внимание, что то, как реализован уровень данных, имеет очень важные последствия; особенно, может ли уровень модели функционировать без уровня данных (например, для модульного тестирования или для дешевых БД в памяти на ноутбуке разработчика вместо дорогих БД Oracle или чего-либо еще, что у вас есть). Но это действительно детали реализации на уровне архитектуры, который мы сейчас рассматриваем. Очевидно, что здесь вы все еще хотите иметь разделение, т.е. я не хотел бы видеть код, который имеет чисто доменную логику, непосредственно чередующуюся с доступом к данным, интенсивно связывая это вместе. Тема для другого вопроса.
Возвращаясь к вашему вопросу: мне кажется, что между вашей новой архитектурой и схемой MVC, которую я описал, существует большое совпадение, так что вы не ошибаетесь, но вы, похоже, тоже изобретаете что-то новое, или используя его, потому что ваша текущая среда программирования / библиотеки предлагают такое. Трудно сказать за меня. Поэтому я не могу дать вам точный ответ о том, что вы намереваетесь, особенно хорошо или плохо. Вы можете узнать, проверив, имеет ли каждая «вещь» только один класс, ответственный за нее; все ли сплоченно и слабо спарено. Это дает вам хорошее представление и, на мой взгляд, достаточно для хорошего ООП-дизайна (или хорошего эталона того же, если хотите).
источник