ситуация
Ранее этим вечером я дал ответ на вопрос о StackOverflow.
Вопрос:
Редактирование существующего объекта должно быть сделано на уровне хранилища или в сервисе?
Например, если у меня есть Пользователь, у которого есть задолженность. Я хочу изменить свой долг. Должен ли я сделать это в UserRepository или в сервисе, например, BuyingService, получив объект, отредактировав его и сохранив?
Мой ответ:
Вы должны оставить ответственность за мутирование объекта с тем же объектом и использовать хранилище для извлечения этого объекта.
Пример ситуации:
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
Комментарий, который я получил:
Бизнес-логика действительно должна быть в сервисе. Не в модели.
Что говорит интернет?
Итак, это заставило меня искать, так как я никогда (сознательно) не использовал уровень обслуживания. Я начал читать по шаблону сервисного уровня и шаблону единицы работы, но пока не могу сказать, что убежден, что сервисный уровень нужно использовать.
Возьмем, к примеру, эту статью Мартина Фаулера об анти-паттерне анемичной доменной модели:
В доменном пространстве есть объекты, многие из которых названы в честь имен существительных, и эти объекты связаны с богатыми отношениями и структурой, которые имеют истинные доменные модели. Уловка возникает, когда вы смотрите на поведение, и вы понимаете, что на этих объектах почти нет поведения, что делает их чуть более мешками добытчиков и сеттеров. Действительно, часто эти модели поставляются с правилами проектирования, которые говорят, что вы не должны помещать какую-либо доменную логику в доменные объекты. Вместо этого есть набор сервисных объектов, которые захватывают всю логику домена. Эти сервисы живут поверх модели домена и используют модель домена для данных.
(...) Логика, которая должна быть в объекте домена, - это логика домена - проверки, вычисления, бизнес-правила - как бы вы это ни называли.
Мне показалось, что именно такова была ситуация: я защищал манипулирование данными объекта, вводя методы внутри этого класса, которые делают именно это. Однако я понимаю, что это должно быть задано в любом случае, и это, вероятно, больше связано с тем, как эти методы вызываются (используя репозиторий).
У меня также было ощущение, что в этой статье (см. Ниже) уровень обслуживания больше рассматривается как фасад, который делегирует работу базовой модели, а не как фактический трудоемкий уровень.
Уровень приложения [его имя для Service Layer]: определяет задания, которые должно выполнять программное обеспечение, и направляет выразительные доменные объекты для решения проблем. Задачи, за которые отвечает этот уровень, значимы для бизнеса или необходимы для взаимодействия с прикладными уровнями других систем. Этот слой держится тонким. Он не содержит бизнес-правил или знаний, а только координирует задачи и делегирует работу для совместной работы объектов домена в следующем слое вниз. Он не имеет состояния, отражающего бизнес-ситуацию, но может иметь состояние, отражающее ход выполнения задачи для пользователя или программы.
Что здесь усилено :
Сервисные интерфейсы. Сервисы предоставляют сервисный интерфейс, на который отправляются все входящие сообщения. Вы можете рассматривать интерфейс службы как фасад, который предоставляет бизнес-логику, реализованную в приложении (обычно логику на бизнес-уровне), потенциальным потребителям.
И здесь :
Уровень обслуживания должен быть лишен какой-либо прикладной или бизнес-логики и должен быть сосредоточен в первую очередь на нескольких проблемах. Он должен обернуть вызовы бизнес-уровня, перевести ваш домен на общий язык, понятный вашим клиентам, и обработать среду связи между сервером и запрашивающим клиентом.
Это серьезный контраст с другими ресурсами, которые говорят о сервисном уровне:
Уровень обслуживания должен состоять из классов с методами, которые являются единицами работы с действиями, которые принадлежат одной и той же транзакции.
Или второй ответ на вопрос, который я уже связал:
В какой-то момент вашему приложению понадобится бизнес-логика. Кроме того, вы можете проверить входные данные, чтобы убедиться, что не запрошено что-то плохое или недействительное. Эта логика принадлежит вашему уровню обслуживания.
"Решение"?
Следуя рекомендациям в этом ответе , я пришел к следующему подходу, использующему уровень обслуживания:
class UserController : Controller {
private UserService _userService;
public UserController(UserService userService){
_userService = userService;
}
public ActionResult MakeHimPay(string username, int amount) {
_userService.MakeHimPay(username, amount);
return RedirectToAction("ShowUserOverview");
}
public ActionResult ShowUserOverview() {
return View();
}
}
class UserService {
private IUserRepository _userRepository;
public UserService(IUserRepository userRepository) {
_userRepository = userRepository;
}
public void MakeHimPay(username, amount) {
_userRepository.GetUserByName(username).makePayment(amount);
}
}
class UserRepository {
public User GetUserByName(string name){
// Get appropriate user from database
}
}
class User {
private int debt; // debt in cents
private string name;
// getters
public void makePayment(int cents){
debt -= cents;
}
}
Заключение
Все вместе мало что изменилось: код контроллера перешел на уровень обслуживания (что хорошо, так что у этого подхода есть свои плюсы). Однако это не похоже на то, что имеет какое-либо отношение к моему первоначальному ответу.
Я понимаю, что шаблоны проектирования - это руководящие принципы, а не правила, установленные в камне, которые должны быть реализованы при любой возможности. Тем не менее, я не нашел однозначного объяснения уровня обслуживания и того, как его следует рассматривать.
Это средство просто извлечь логику из контроллера и поместить ее в службу вместо этого?
Предполагается ли сформировать договор между контроллером и доменом?
Должен ли быть слой между доменом и уровнем сервиса?
И, наконец, что не менее важно: после оригинального комментария
Бизнес-логика действительно должна быть в сервисе. Не в модели.
Это верно?
- Как бы я представил свою бизнес-логику в сервисе вместо модели?
источник
Ответы:
Чтобы определить обязанности службы , сначала необходимо определить, что это за служба .
Услуга не является каноническим или общим программным термином. На самом деле суффикс
Service
имени класса во многом похож на злобный Менеджер : он почти ничего не говорит о том, что на самом деле делает объект .В действительности то, что должен делать сервис, сильно зависит от архитектуры:
В традиционной многоуровневой архитектуре сервис буквально ассоциируется с уровнем бизнес-логики . Это слой между пользовательским интерфейсом и данными. Поэтому все бизнес-правила переходят в сервисы. Уровень данных должен понимать только основные операции CRUD, а уровень пользовательского интерфейса должен иметь дело только с отображением DTO представления в бизнес-объектах и из них.
В распределенной архитектуре в стиле RPC (SOAP, UDDI, BPEL и т. Д.) Служба является логической версией физической конечной точки . По сути, это набор операций, которые сопровождающий желает предоставить в качестве общедоступного API. Различные руководства по передовому опыту объясняют, что на самом деле операция сервиса должна быть операцией бизнес-уровня, а не CRUD, и я склонен согласиться.
Однако, поскольку маршрутизация всего через реальный удаленный сервис может серьезно снизить производительность, обычно лучше не делать, чтобы эти сервисы сами реализовывали бизнес-логику; вместо этого им следует обернуть «внутренний» набор бизнес-объектов. Один сервис может включать один или несколько бизнес-объектов.
В архитектуре MVP / MVC / MVVM / MV * сервисы вообще не существуют. Или, если они это делают, термин используется для обозначения любого общего объекта, который может быть введен в контроллер или модель представления. Бизнес-логика в вашей модели . Если вы хотите создать «служебные объекты» для организации сложных операций, это рассматривается как деталь реализации. К сожалению, многие люди реализуют MVC подобным образом, но это считается анти-паттерном ( Anemic Domain Model ), потому что сама модель ничего не делает, это просто набор свойств для пользовательского интерфейса.
Некоторые люди ошибочно полагают, что использование метода с 100-строчным контроллером и внедрение его в службу каким-то образом приводит к лучшей архитектуре. Это действительно не так; все, что он делает, это добавляет еще один, возможно, ненужный слой косвенности. Практически говоря, контроллер все еще выполняет свою работу, он просто делает это с помощью плохо названного объекта-помощника. Я настоятельно рекомендую презентацию Wimed Domain Models Джимми Богарда для наглядного примера того, как превратить анемичную модель предметной области в полезную. Это включает в себя тщательное изучение моделей, которые вы выставляете, и какие операции действительно действительны в бизнес- контексте.
Например, если ваша база данных содержит «Заказы», и у вас есть столбец «Общая сумма», вашему приложению, вероятно, нельзя разрешить фактически изменять это поле на произвольное значение, поскольку (а) это история и (б) оно должно быть определяется тем, что в порядке, а также, возможно, некоторыми другими чувствительными ко времени данными / правилами. Создание службы для управления заказами не обязательно решает эту проблему, потому что пользовательский код может по- прежнему захватывать фактический объект заказа и изменять сумму на нем. Вместо этого, порядок сам должен нести ответственность за обеспечение того, что он может быть изменен только в безопасных и последовательных способах.
В DDD сервисы предназначены специально для ситуации, когда у вас есть операция, которая должным образом не принадлежит какому-либо агрегированному корню . Здесь нужно быть осторожным, потому что часто потребность в услуге может означать, что вы не использовали правильные корни. Но, предположив, что вы это сделали, сервис используется для координации операций между несколькими корнями или иногда для решения проблем, которые вообще не затрагивают модель предметной области (например, возможно, запись информации в базу данных BI / OLAP).
Одним из примечательных аспектов службы DDD является то, что ей разрешено использовать сценарии транзакций . Работая с большими приложениями, вы, скорее всего, в конечном итоге столкнетесь с случаями, когда просто выполнить что-либо с помощью процедуры T-SQL или PL / SQL намного проще, чем с моделью предметной области. Это нормально, и это относится к Сервису.
Это радикальный отход от определения сервисов многоуровневой архитектуры. Сервисный уровень инкапсулирует доменные объекты; служба DDD инкапсулирует все, что не входит в доменные объекты, и не имеет смысла быть.
В сервис-ориентированной архитектуре сервис считается техническим органом, обеспечивающим возможности бизнеса. Это означает, что он является эксклюзивным владельцем определенного подмножества бизнес-данных, и ничто другое не может касаться этих данных - даже просто читать их.
По необходимости, сервисы на самом деле являются сквозным предложением в SOA. Это означает, что сервис - это не столько отдельный компонент, сколько целый стек , и все ваше приложение (или весь ваш бизнес) представляет собой набор этих сервисов, работающих бок о бок без пересечений, за исключением уровней обмена сообщениями и пользовательского интерфейса. Каждый сервис имеет свои данные, свои бизнес-правила и собственный интерфейс. Им не нужно взаимодействовать друг с другом, потому что они должны быть ориентированы на бизнес - и, как и сам бизнес, каждый сервис имеет свой собственный набор обязанностей и работает более или менее независимо от других.
Таким образом, согласно определению SOA, каждая часть бизнес-логики в любом месте содержится внутри службы, но опять же, как и вся система . Сервисы в SOA могут иметь компоненты , и они могут иметь конечные точки , но довольно опасно называть любой фрагмент кода сервисом, потому что он конфликтует с тем, что должно означать исходное «S».
Поскольку SOA, как правило, очень заинтересован в обмене сообщениями, операции, которые вы, возможно, ранее упаковывали в сервис , обычно инкапсулированы в обработчиках , но их кратность различна. Каждый обработчик обрабатывает один тип сообщения, одну операцию. Это строгое толкование принципа единой ответственности , но оно обеспечивает большую ремонтопригодность, поскольку каждая возможная операция относится к своему классу. Таким образом, вам на самом деле не нужна централизованная бизнес-логика, потому что команды представляют бизнес-операции, а не технические.
В конечном счете, в любой архитектуре, которую вы выберете, будет какой-то компонент или слой, который имеет большую часть бизнес-логики. В конце концов, если бизнес-логика разбросана повсюду, то у вас просто есть код для спагетти. Но то, называете ли вы этот компонент службой или как он разработан с точки зрения таких вещей, как количество или размер операций, зависит от ваших архитектурных целей.
Там нет правильного или неправильного ответа, только то, что относится к вашей ситуации.
источник
Что касается вашего названия , я не думаю, что вопрос имеет смысл. Модель MVC состоит из данных и бизнес-логики. Сказать, что логика должна быть в Сервисе, а не Модель, это как сказать: «Пассажир должен сидеть на сиденье, а не в машине».
Опять же, термин «модель» является перегруженным термином. Возможно, вы имели в виду не модель MVC, а модель в смысле объекта передачи данных (DTO). АКА Сущность. Об этом говорит Мартин Фаулер.
Как я понимаю, Мартин Фаулер говорит о вещах в идеальном мире. В реальном мире Hibernate и JPA (на земле Java) DTO - это супер утечка абстракций. Я хотел бы поместить мою бизнес-логику в мою сущность. Это сделало бы вещи чище. Проблема в том, что эти объекты могут существовать в управляемом / кэшированном состоянии, которое очень сложно понять и постоянно мешает вашим усилиям. Подводя итог моему мнению: Мартин Фаулер рекомендует правильный путь, но ОРМ мешают вам это сделать.
Я думаю, что у Боба Мартина есть более реалистичное предложение, и он дает его в этом видео, которое не бесплатно . Он говорит о том, чтобы держать ваши DTO свободными от логики. Они просто хранят данные и переносят их на другой уровень, который гораздо более объектно-ориентирован и не использует DTO напрямую. Это позволяет избежать утечки абстракции от вас кусать. Слой с DTO и самими DTO не являются OO. Но как только вы выходите из этого слоя, вы становитесь таким же OO, как и Мартин Фаулер.
Преимущество этого разделения состоит в том, что оно абстрагирует постоянный слой. Вы можете переключиться с JPA на JDBC (или наоборот), и никакая бизнес-логика не должна будет измениться. Это зависит только от DTO, не важно, как эти DTO заполняются.
Чтобы немного изменить тему, необходимо учитывать тот факт, что базы данных SQL не являются объектно-ориентированными. Но ORM обычно имеют сущность - которая является объектом - для каждой таблицы. Итак, с самого начала вы уже проиграли битву. По моему опыту, вы никогда не можете представлять сущность в точности так, как вы хотите, объектно-ориентированным способом.
Что касается « на службе», Боб Мартин был бы против того , чтобы класс с именем
FooBarService
. Это не объектно-ориентированный. Что делает сервис? Все, что связано сFooBars
. Это может также быть помеченоFooBarUtils
. Я думаю, что он будет выступать за уровень обслуживания (более подходящее название - уровень бизнес-логики), но у каждого класса в этом уровне будет значимое имя.источник
Я сейчас работаю над новым проектом, и нам пришлось принять несколько архитектурных решений только вчера. Как ни странно, мне пришлось вернуться к нескольким главам «Шаблоны архитектуры корпоративных приложений».
Вот что мы придумали:
В итоге мы получаем следующее:
Клиент -> Сервис -> Домен -> Данные
Мы можем заменить клиент, сервис или уровень данных разумным объемом работы. Если логика вашего домена жила в службе, и вы решили, что хотите заменить или даже удалить уровень службы, вам придется переместить всю бизнес-логику куда-нибудь еще. Такое требование встречается редко, но это может произойти.
Сказав все это, я думаю, что это довольно близко к тому, что имел в виду Мартин Фаулер, говоря
Диаграмма ниже хорошо это иллюстрирует:
http://martinfowler.com/eaaCatalog/serviceLayer.html
источник
Это одна из тех вещей, которая действительно зависит от варианта использования. Общая цель сервисного уровня - объединить бизнес-логику. Это означает, что несколько контроллеров могут вызывать один и тот же UserService.MakeHimPay (), фактически не заботясь о том, как производится оплата. То, что происходит в службе, может быть таким же простым, как изменение свойства объекта, или оно может выполнять сложную логику, связанную с другими службами (т. Е. Вызывать сторонние службы, вызывать логику проверки или даже просто сохранять что-то в базе данных). )
Это не означает, что вы должны удалить ВСЕ логику из доменных объектов. Иногда более разумно, чтобы метод для объекта предметной области выполнял некоторые вычисления сам по себе. В последнем примере служба представляет собой избыточный уровень поверх объекта репозитория / домена. Это обеспечивает хороший буфер против изменений требований, но это действительно не обязательно. Если вы считаете, что вам нужен сервис, попробуйте выполнить простую логику «изменить свойство X объекта Y» вместо объекта домена. Логика на предметных классах имеет тенденцию попадать в «вычислить это значение из полей», а не выставлять все поля через геттеры / сеттеры.
источник
Самый простой способ проиллюстрировать, почему программисты избегают помещать доменную логику в доменные объекты, состоит в том, что они обычно сталкиваются с ситуацией «где я могу разместить логику проверки?» Возьмите этот объект домена, например:
Таким образом, у нас есть некоторая базовая логика проверки в установщике (не может быть отрицательной). Проблема в том, что вы не можете использовать эту логику повторно. Где-то есть экран, или ViewModel, или контроллер, который должен выполнить проверку перед тем, как он фактически передаст изменения в объект домена, потому что он должен проинформировать пользователя либо до, либо когда он нажмет кнопку «Сохранить», что он не может этого сделать, и почему . Тестирование исключения, когда вы вызываете сеттер, является уродливым хаком, потому что вы действительно должны были выполнить всю проверку до того, как начали транзакцию.
Вот почему люди переносят логику валидации на какую-то услугу, например
MyEntityValidator
. Затем объект и логика вызова могут получить ссылку на службу проверки и повторно использовать ее.Если вы этого не сделаете и все еще хотите повторно использовать логику проверки, вы в конечном итоге поместите ее в статические методы класса сущности:
Это сделало бы вашу модель предметной области менее «анемичной» и оставило бы логику проверки рядом со свойством, что замечательно, но я не думаю, что кому-то действительно нравятся статические методы.
источник
Я думаю, что ответ ясен, если вы прочитаете статью Мартина Фаулера «Модель анемичного домена» .
Удаление бизнес-логики, которая является доменом, из модели предметной области существенно нарушает объектно-ориентированное проектирование.
Давайте рассмотрим основную объектно-ориентированную концепцию: объект инкапсулирует данные и операции. Например, закрытие учетной записи - это операция, которую объект учетной записи должен выполнить для себя; следовательно, наличие уровня обслуживания для выполнения этой операции не является объектно-ориентированным решением. Это процедурный процесс, и именно об этом говорит Мартин Фаулер, когда говорит о модели анемичной области.
Если у вас есть сервисный слой, закрывающий учетную запись, а не закрывающий сам объект учетной записи, у вас нет реального объекта учетной записи. Ваша учетная запись "объект" является просто структурой данных. В конечном итоге, как предлагает Мартин Фаулер, получается куча сумок с геттерами и сеттерами.
источник
Как бы вы реализовали свою бизнес-логику на уровне сервиса? Когда вы делаете платеж от пользователя, вы создаете платеж, а не просто вычитаете стоимость из свойства.
Ваш способ оплаты должен создать запись о платеже, добавить к долгу этого пользователя и сохранить все это в ваших репозиториях. Выполнение этого в методе обслуживания невероятно просто, и вы также можете заключить всю операцию в транзакцию. Делать то же самое в модели агрегированных доменов гораздо сложнее.
источник
Версия tl; dr.
Мой опыт и мнения говорят о том, что любые объекты, имеющие бизнес-логику, должны быть частью модели предметной области. Модель данных, скорее всего, не должна иметь никакой логики. Скорее всего, службы должны связывать их вместе и решать общие проблемы (базы данных, ведение журналов и т. Д.). Однако принятый ответ является наиболее практичным.
Более длинная версия, на которую ссылались другие, заключается в том, что слово «модель» является двусмысленным. Сообщение переключается между моделью данных и моделью предметной области, как будто они одинаковы, что является очень распространенной ошибкой. Там также может быть легкое двусмысленность в слове "служба".
На практике у вас не должно быть службы, которая вносит изменения в какие-либо доменные объекты; причина этого в том, что у вашей службы, вероятно, будет какой-то метод для каждого свойства вашего объекта, чтобы изменить значение этого свойства. Это проблема, потому что тогда, если у вас есть интерфейс для вашего объекта (или даже если нет), сервис больше не следует принципу Open-Closed; вместо этого, всякий раз, когда вы добавляете больше данных в вашу модель (независимо от домена в сравнении с данными), вам в конечном итоге приходится добавлять дополнительные функции в вашу службу. Есть определенные способы обойти это, но это самая распространенная причина, по которой я видел сбой «корпоративных» приложений, особенно когда эти организации считают, что «корпоративное» означает «иметь интерфейс для каждого объекта в системе». Можете ли вы представить добавление новых методов в интерфейс, затем к двум или трем различным реализациям (встроенная в приложение, фиктивная реализация и отладочная, встроенная в память?), только для одного свойства вашей модели? Звучит как ужасная идея для меня.
Здесь есть более длинная проблема, в которую я не буду вдаваться, но суть в следующем: хардкорное объектно-ориентированное программирование говорит, что никто за пределами релевантного объекта не должен иметь возможности изменять значение свойства внутри объекта или даже " см. «стоимость имущества внутри объекта. Это можно облегчить, сделав данные доступными только для чтения. Вы по-прежнему можете столкнуться с такими проблемами, как, например, когда многие люди используют данные даже для чтения, и вам приходится менять тип этих данных. Возможно, что все потребители должны будут измениться, чтобы приспособиться к этому. Вот почему, когда вы делаете API-интерфейс для использования кем-то и всеми, вам советуют не иметь общедоступных или даже защищенных свойств / данных; это и есть та самая причина, по которой ООП был изобретен.
Я думаю, что большинство ответов здесь, за исключением того, который отмечен как принятый, все затуманивают проблему. Тот, который отмечен как принятый, хорош, но я все еще чувствовал необходимость ответить и согласиться с тем, что пункт 4 - это путь, в общем.
источник
Ответ в том, что это зависит от варианта использования. Но в большинстве общих сценариев я бы придерживался бизнес-логики, лежащей на уровне обслуживания. Приведенный вами пример действительно прост. Однако, как только вы начнете думать о разделенных системах или сервисах и добавляете к ним транзакционное поведение, вы действительно захотите, чтобы это произошло как часть сервисного уровня.
Источники, которые вы указали для уровня обслуживания, лишенного какой-либо бизнес-логики, представляют другой уровень, который является бизнес-уровнем. Во многих сценариях сервисный уровень и бизнес-уровень сжимаются в одно целое. Это действительно зависит от того, как вы хотите создать свою систему. Вы можете выполнить работу в три слоя и продолжать украшать и добавлять шум.
В идеале вы можете использовать сервисы моделей, которые включают в себя бизнес-логику для работы с моделями доменов для сохранения состояния . Вы должны попытаться разделить услуги как можно больше.
источник
В MVC модель определяется как бизнес-логика. Утверждать, что это должно быть где-то еще, неверно, если только он не использует MVC. Я рассматриваю уровни обслуживания как похожие на модульную систему. Это позволяет вам связать набор связанных функций в хороший пакет. Внутренние элементы этого сервисного уровня будут иметь модель, выполняющую ту же работу, что и ваша.
источник
Концепция сервисного уровня может рассматриваться с точки зрения DDD. Аарона упомянул об этом в своем ответе, я просто уточню это немного.
Общий подход заключается в том, чтобы иметь контроллер, специфичный для типа клиента. Скажем, это может быть веб-браузер, это может быть какое-то другое приложение, это может быть функциональный тест. Форматы запросов и ответов могут отличаться. Поэтому я использую сервис приложений в качестве инструмента для использования гексагональной архитектуры . Я внедряю туда классы инфраструктуры, специфичные для конкретного запроса. Например, мой контроллер, обслуживающий запросы веб-браузера, может выглядеть так:
Если я пишу функциональный тест, я хочу использовать фальшивый платежный клиент и, вероятно, мне не понадобится HTML-ответ. Так что мой контроллер может выглядеть так:
Итак, сервис приложений - это среда, которую я настроил для запуска бизнес-логики. Именно здесь и называются модельные классы - независимо от реализации инфраструктуры.
Итак, отвечая на ваши вопросы с этой точки зрения:
Нет.
Ну, это можно так назвать.
Нет.
Существует радикально другой подход, который полностью отрицает использование любого вида услуг. Например, Дэвид Уэст в своей книге « Мышление объекта» утверждает, что любой объект должен иметь все необходимые ресурсы для выполнения своей работы. Этот подход приводит, например, к отказу от любого ORM .
источник
Для записи.
SRP:
В этом случае можно выполнить следующие действия:
Если задолженность не потребует каких-либо расчетов:
Однако, если это требует некоторого вычисления тогда:
или также
Но также, если вычисление выполняется на постоянном уровне, такая процедура хранения
В этом случае, если мы хотим извлечь пользователя из базы данных и затем обновить долг, мы должны сделать это в несколько этапов (на самом деле, два), и не требуется заключать / инкапсулировать его в функцию службы.
И если требуется сохранить его, мы можем добавить третий шаг
О предлагаемом решении
а) Мы не должны бояться оставлять конечного разработчика, чтобы написать пару, а не заключать их в функцию.
б) Что касается интерфейса, то некоторые разработчики любят интерфейс, и они в порядке, но в некоторых случаях они вообще не нужны.
c) Цель службы - создать службу без атрибутов, главным образом потому, что мы можем использовать функции Shared / Static. Это также легко для модульного тестирования.
источник
"We shouldn't be afraid to left the end-developer to write a couple of instead of encapsulate it in a function.
«Я могу цитировать только Льюиса Блэка», если бы не моя лошадь, которую я бы не провел в этом году в колледже ».