Тип 0 используется в Event Sourcing (ES). Может сложиться ощущение что ES это Тип 2, но это не так. Потому что, ES разделяет понятие события и агрегат. Агрегат это искомая бизнес сущность, например документ. Событие же это то что произошло с документом и оно не изменно. Этьо уже произошло и не должно никогда меняться. Если хотим изменить просто создаем новое событие «А теперь хотим изменить поле на какое-то значение».
Сам факт наличие событий является историей данного документа. ES гораздо больше чем версионность, т.к. события могут говорить не только об изменениях в документе, но и других процессах (документ без изменений был согласован; документ без изменений перешел в архив и т.п.).
Поэтому Тип 0 во всю используется в современном стеке технологий.
Но это так дополнение, спасибо за статью :)
Спасибо за статью, полностью согласен с автором и с принципом SRP. Однако размышляя о SRP и ища в коде хорошие и наглядные примеры когда SRP помогает а когда усложняет, я наткнулся на класс List и у него полно методов и полный набор CRUD и кучу перегрузок по каждой букве из CRUD.
И вот вопрос, нужно что делать отдельный класс структура данных и отдельные классы реализациий каждого метода?
Это же будет дико не удобно:
public class MyClass{
public MyClass(IListAdder adder, IListRemover remover)
public void MyMethod(IEnumerable<int> list){
remover.Remove(10, list);
adder.Add(12, list);
}
}
Однако не всегда получается красиво так выделить функционал в отдельный класс. Иногда, бывает так, что приватный метод, который нужно сделать protected сильно завязан на члены класса Base.
И ладно еще если метод зависит от публичных членов класса Base, тогда можно было бы вызывать метод в контексте Base, таким образом
function SomeSpeciaFunctionality(){
this.method = function (){ // useful code
}
}
function Base(){
var _self = this;
var _compositionInstance = new SomeSpeciaFunctionality();
function myPrivateFunction(){
_compositionInstance.method.call(_self);
}
}
хуже, когда защищаемый метод зависит от приватных, т.е. замкнутых членов класса Base.
Хотя и это не смертельно, можно отказаться от реализации приватных членов через замыкание и использовать замкнутый приватный контекст и на нем уже вызывать:
function SomeSpeciaFunctionality(privateMembers){
this.method = function (){ // useful code
}
}
function Base(){
var _self = this;
var _privateMembers ={
myPrivateVariable: 12
}
var _compositionInstance = new SomeSpeciaFunctionality(_privateMembers);
function myPrivateFunction(){
_compositionInstance.method.call(_self);
}
}
Из долгих дебатов на тему нужны ли в классах члены с protected-доступом мне показалось разумным аргумент, что вместо того, чтобы разделять приватные члены с производными классами (а ведь protected-доступ, по сути, именно это и означает), нужно создавать новые классы и использовать композицию. И эти новые классы, можно было бы разделять между производными классами.
т.е. вместо
function Base(){
//@protected как бы делаем метод защищенным и разделяем его с производными классами
function protectedMethod(){}
}
разумно сделать
function SomeSpeciaFunctionality(){
this.method = function (){ // useful code
}
}
function Base(){
var _compositionInstance = new SomeSpeciaFunctionality();
function myPrivateFunction(){
_compositionInstance.method();
}
}
function DerivedClass(){
var _compositionInstance = new SomeSpeciaFunctionality();
function anotherPrivateFunction(){
_compositionInstance.method();
}
}
Согласен! И т.к. нарастание копи-пастов не происходит организованно, комментари о том откуда/куда скопированно будет уместен, чтобы понимать сколько раз копировали. И если возникает надобность третий раз коппировать, то нужно помечать код опасным и кондидатом на рефакторинг.
Мне кажется, если DRY не стыкуется с SOLID, то стоит посоветоваться с GoF.
Это повод провести дополнительное тестирование и четко определить как именно должна работать данная логика для срочных посылок
Это правда, так нужно делать обязательно. Однако набор тестов (тест-кейсов) будет зависить от того кода который тестируем, и я думаю комментарии c пометкой какие классы вовлечены в копи-пасту помогли бы как-раз определить этот набор тестов. Да и вообще подобные комментарий откуда/куда копировалось, помогают держать копи-паст под контролем.
В описываемом вами случае можно ввести некий GenericParcel: IParcel, куда сложить всю логику, которая не зависит от типа посылок и затем уже наследовать Parcel и UrgentParcel от него
В случаи, когда мы знаем что надо проектировать одновремено обычные посылки и срочные, то согласен. Но в случаи когда срочные посылки появились позже, рефакторить и как либо менять класс Parcel было бы не разумным риском обрушить модули работы обычных посылок.
P.S. Представьте микросервисную архитектуру, в случаи когда обычные посылки работают в своем микросервисе, мы реализовываем работу срочных посылок в отдельном своем микросервисе и что делать с кодом ела копировать? я думаю это было бы разумнее, чем менять микросервис обычных посылок.
Т е формальные аргументы и возвращаемое значение не меняются, но при этом меняется семантика, так что клиенты IParcel более не могут работать с этим новым классом.
Тут Вы скорее описываете нарушение принципа замещения Лисков.
P.S. Я не хотел описывать сразу весь интерфейс IParcel со всеми его свойствами и методами, дабы читателю было проще читать. Метод ArrivedToRecipient был добавлен не только в класс, он, как я предполагал, но не описал в статье, был изначально у IParcel и его реализация есть в классе Parcel. Так вот вопрос обсуждался стоит ли копи/пастить тело метода в класс UrgentParcel.
Сам факт наличие событий является историей данного документа. ES гораздо больше чем версионность, т.к. события могут говорить не только об изменениях в документе, но и других процессах (документ без изменений был согласован; документ без изменений перешел в архив и т.п.).
Поэтому Тип 0 во всю используется в современном стеке технологий.
Но это так дополнение, спасибо за статью :)
И вот вопрос, нужно что делать отдельный класс структура данных и отдельные классы реализациий каждого метода?
Это же будет дико не удобно:
И ладно еще если метод зависит от публичных членов класса Base, тогда можно было бы вызывать метод в контексте Base, таким образом
хуже, когда защищаемый метод зависит от приватных, т.е. замкнутых членов класса Base.
Хотя и это не смертельно, можно отказаться от реализации приватных членов через замыкание и использовать замкнутый приватный контекст и на нем уже вызывать:
т.е. вместо
разумно сделать
Точно!!! :)
Это правда, так нужно делать обязательно. Однако набор тестов (тест-кейсов) будет зависить от того кода который тестируем, и я думаю комментарии c пометкой какие классы вовлечены в копи-пасту помогли бы как-раз определить этот набор тестов. Да и вообще подобные комментарий откуда/куда копировалось, помогают держать копи-паст под контролем.
В случаи, когда мы знаем что надо проектировать одновремено обычные посылки и срочные, то согласен. Но в случаи когда срочные посылки появились позже, рефакторить и как либо менять класс Parcel было бы не разумным риском обрушить модули работы обычных посылок.
P.S. Представьте микросервисную архитектуру, в случаи когда обычные посылки работают в своем микросервисе, мы реализовываем работу срочных посылок в отдельном своем микросервисе и что делать с кодом ела копировать? я думаю это было бы разумнее, чем менять микросервис обычных посылок.
Спасибо Вам за комментарий,
Тут Вы скорее описываете нарушение принципа замещения Лисков.P.S. Я не хотел описывать сразу весь интерфейс IParcel со всеми его свойствами и методами, дабы читателю было проще читать. Метод ArrivedToRecipient был добавлен не только в класс, он, как я предполагал, но не описал в статье, был изначально у IParcel и его реализация есть в классе Parcel. Так вот вопрос обсуждался стоит ли копи/пастить тело метода в класс UrgentParcel.