При использовании принципа единой ответственности, что представляет собой «ответственность»?

Кажется довольно очевидным, что «принцип единой ответственности» не означает «делает только одно». Для этого нужны методы.

открытый интерфейс CustomerCRUD
{
    public void Create (Клиент клиента);
    открытый клиент Read (int CustomerID);
    public void Update (Клиент клиента);
    public void Удалить (int CustomerID);
}

Боб Мартин говорит, что «классы должны иметь только одну причину для изменения». Но это сложно обдумать, если вы программист, новый для SOLID.

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

Итак, как вы это делаете? Как вы определяете, какие обязанности должен выполнять каждый класс, и как вы определяете ответственность в контексте SRP?

186 голосов | спросил Robert Harvey 27 MarpmMon, 27 Mar 2017 22:32:36 +03002017-03-27T22:32:36+03:0010 2017, 22:32:36

16 ответов


108

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

Например:

  

Новое деловое требование: Пользователи, находящиеся в Калифорнии, получают специальную скидку.

     

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

     

Пример плохих изменений: мне нужно изменить код в классе User, и это изменение будет иметь каскадный эффект для других классов, которые используют класс User, включая классы, которые не имеют никакого отношения к скидкам, например. регистрации, перечисления и управления.

Или:

  

Новое нефункциональное требование: мы начнем использовать Oracle вместо SQL Server

     

Пример хороших изменений: просто нужно изменить один класс на уровне доступа к данным, который определяет, как сохранить данные в DTO.

     

Плохое изменение: мне нужно изменить все классы моего бизнес-уровня, поскольку они содержат логику, специфичную для SQL Server.

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

Как минимум, ваши классы должны отделять логические проблемы от физических проблем. Большой ряд примеров можно найти в пространстве имен System.IO: там можно найти различные виды физических потоков (например, FileStream, MemoryStream , или NetworkStream) и различные читатели и писатели (BinaryWriter, TextWriter)), которые работают на логическом уровне. Разделив их таким образом, мы избегаем комбинаторного взрыва: вместо необходимости FileStreamTextWriter, FileStreamBinaryWriter, NetworkStreamTextWriter, NetworkStreamBinaryWriter, MemoryStreamTextWriter и MemoryStreamBinaryWriter, вы просто подключаете автора и поток, и у вас может быть то, что вы хотите. Затем мы можем добавить, скажем, XmlWriter, не требуя повторной реализации его для памяти, файла и сети по отдельности.

ответил John Wu 27 MarpmMon, 27 Mar 2017 22:51:51 +03002017-03-27T22:51:51+03:0010 2017, 22:51:51
71

Практически говоря, ответственность ограничена теми вещами, которые вероятно меняются. . Таким образом, нет научного или формульного способа достичь того, что составляет ответственность, к сожалению. Это решение.

Это о том, что в вашем опыте , скорее всего, изменится.

Мы склонны применять язык принципа в гиперболической, буквальной, ревностной ярости. Мы склонны разделять классы, потому что они могут меняться, или вдоль линий, которые просто помогают нам нарушать проблемы. (Последняя причина не по своей природе плохая.) Но SRP не существует ради него самого; он обслуживает создание поддерживающего программного обеспечения .

Итак, если деления не управляются изменениями вероятных , они не являются true при обслуживании SRP 1 , если YAGNI более применим. Оба служат одной и той же конечной цели. И оба - это вопросы суждения - надеюсь, приправленный суждение.

Когда дядя Боб пишет об этом, он предлагает подумать о «ответственности» в терминах «кто просит перемен». Другими словами, мы не хотим, чтобы партия А потеряла работу, потому что партия Б попросила внести изменения.

  

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

Хорошие и опытные разработчики будут иметь смысл, для которого возможны изменения. И этот ментальный список будет несколько отличаться от отрасли и организации.

То, что составляет ответственность в вашем конкретном приложении, в вашей конкретной организации, в конечном счете является вопросом приправленного . Речь идет о том, что может измениться. И, в некотором смысле, речь идет о , которому принадлежит внутренняя логика модуля.


1. Чтобы быть ясным, это не означает, что они являются bad . Они могут быть большими подразделениями, которые значительно улучшают читаемость кода. Это просто означает, что они не управляются SRP.

ответил svidgen 28 MaramTue, 28 Mar 2017 00:00:01 +03002017-03-28T00:00:01+03:0012 2017, 00:00:01
26

Я следую «классы должны иметь только одну причину для изменения».

Для меня это означает мышление о закоренелых схемах, которые может возникнуть у моего владельца продукта («Мы должны поддерживать мобильный!», «Нам нужно пойти в облако!», «Мы должны поддерживать китайцев!») , Хорошие проекты ограничивают влияние этих схем на более мелкие области и делают их относительно легко выполненными. Плохие проекты означают, что вы собираетесь совершить множество кодов и сделать кучу рискованных изменений.

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

Практически я нахожу, что здесь помогают юнит-тесты. Если ваш код негибкий, это будет трудно проверить. Если вы не можете вводить mocks или другие данные теста, вы, вероятно, не сможете ввести код SupportChinese.

Еще одна приблизительная метрика - шаг лифта. Традиционные лифты - «если бы вы были в лифте с инвестором, можете ли вы продать его по идее?». Стартапы должны иметь простые, короткие описания того, что они делают - в чем их основное внимание. Аналогично, классы (и функции) должны иметь простое описание того, что они делают . Не «этот класс реализует некоторый фубар, который вы можете использовать в этих конкретных сценариях». Что-то, что вы можете сказать другому разработчику: «Этот класс создает пользователей». Если вы не можете сообщить об этом другим разработчикам, вы можете go получить ошибки.

ответил Telastyn 27 MarpmMon, 27 Mar 2017 23:23:29 +03002017-03-27T23:23:29+03:0011 2017, 23:23:29
23

Никто не знает. Или, по крайней мере, мы не можем договориться об одном определении. Именно это делает SPR (и другие принципы SOLID) довольно противоречивыми.

Я бы сказал, что возможность выяснить, что является или не несет ответственности, - это один из навыков, который разработчик программного обеспечения должен изучать на протяжении своей карьеры. Чем больше кода вы пишете и просматриваете, тем больше опыта вы должны будете определить, есть ли что-то одно или несколько обязанностей. Или, если одна отдельная ответственность раздроблена по отдельным частям кода.

Я бы сказал, что основная цель SRP не должна быть жестким правилом. Это напоминание о том, что мы должны помнить о когезии в коде и всегда прилагать определенные усилия для определения того, какой код является сплоченным, а что нет.

ответил Euphoric 27 MarpmMon, 27 Mar 2017 23:24:56 +03002017-03-27T23:24:56+03:0011 2017, 23:24:56
5

Я думаю, что термин «ответственность» полезен как метафора, потому что он позволяет нам использовать программное обеспечение для исследования того, насколько хорошо организовано программное обеспечение. В частности, я бы остановился на двух принципах:

  • Ответственность соизмерима с полномочиями.
  • Ни одно из двух сущностей не должно отвечать за одно и то же.

Эти два принципа позволят нам осознать ответственность, потому что они играют друг с другом. Если вы уполномочиваете часть кода делать что-то для себя, она должна нести ответственность за то, что она делает. Это приводит к тому, что класс должен расти, расширяя его «одну причину изменения» на более широкие и широкие сферы. Однако, поскольку вы делаете вещи шире, вы, естественно, начинаете сталкиваться с ситуациями, когда несколько субъектов несут ответственность за одно и то же. Это чревато проблемами в реальной жизни, так что, конечно, это проблема кодирования. В результате этот принцип заставляет узлы сужаться, поскольку вы подразделяете ответственность на не дублированные посылки.

В дополнение к этим двум, третий принцип кажется разумным:

  • Ответственность может быть делегирована

Рассмотрим свежеиспеченную программу ... чистый лист. Сначала у вас есть только одна сущность, которая является программой в целом. Он несет ответственность за ... все. Естественно, в какой-то момент вы начнете делегировать ответственность за функции или классы. На этом этапе вступают в силу первые два правила, заставляющие вас балансировать эту ответственность. Программа высшего уровня по-прежнему отвечает за общий результат, точно так же, как менеджер несет ответственность за производительность своей команды, но каждому подразделению была делегирована ответственность, а вместе с ним и полномочия на выполнение этой ответственности.

В качестве дополнительного бонуса это делает SOLID особенно совместимым с любой корпоративной разработкой программного обеспечения, которую может потребоваться. У каждой компании на планете есть понятие о том, как делегировать ответственность, и они не все согласны. Если вы делегируете ответственность в своем программном обеспечении таким образом, который напоминает вашу собственную делегацию вашей компании, будущим разработчикам будет намного легче справляться с тем, как вы делаете это в этой компании.

ответил Cort Ammon 29 MaramWed, 29 Mar 2017 00:17:54 +03002017-03-29T00:17:54+03:0012 2017, 00:17:54
5

В этой конференции в Йеле, Дядя Боб дает этот пример прикольный :

 Введите описание изображения здесь

Он говорит, что Employee имеет три причины для изменения, три источника требований к изменениям и дает этот юмористический и язык в щеке , но, тем не менее, иллюстрацию:

  
  • Если метод CalcPay () имеет ошибку и стоит компании в миллионы долларов США, , финансовый директор уволит вас .

  •   
  • Если метод ReportHours () имеет ошибку и стоит компании в миллионы долларов США, , то COO уволит вас .

  •   
  • Если метод WriteEmmployee () имеет ошибку, которая приводит к стиранию большого количества данных и затрат компании на миллионы долларов США,   CTO уволит вас .

  •   

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

Он дает это решение, которое разрешает нарушение SRP, но все же должно решить нарушение DIP, которое не показано в видео.

 Введите описание изображения здесь

ответил Tulains Córdova 29 MaramWed, 29 Mar 2017 02:47:55 +03002017-03-29T02:47:55+03:0002 2017, 02:47:55
3

Я думаю, что лучший способ подразделить вещи, чем «причины для изменения», состоит в том, чтобы начать думать о том, имеет ли смысл требовать, чтобы код, который должен выполнять, выполнял два (или более) действия, должен был отдельную ссылку на объект для каждого действия, и было бы полезно иметь публичный объект, который мог бы сделать одно действие, но не другое.

Если ответы на оба вопроса да, это предполагает, что действия должны выполняться отдельными классами. Если ответов на оба вопроса нет, это предполагает, что с публичной точки зрения должен быть один класс; если код для этого будет громоздким, он может быть внутренне разделен на частные классы. Если ответ на первый вопрос отсутствует, но второй - да, для каждого действия plus должен быть отдельный класс, включающий ссылки на экземпляры других.

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

Можно сказать, что «ответственность» каждого класса заключается в том, чтобы включить какую-то реальную логику или предоставить общую точку привязки для нескольких других классов, которые делают это, но важно сосредоточиться в первую очередь на том, как должен выглядеть клиентский код класс. Если для клиентского кода имеет смысл увидеть что-то как один объект, тогда код клиента должен рассматривать его как отдельный объект.

ответил supercat 28 MaramTue, 28 Mar 2017 02:01:15 +03002017-03-28T02:01:15+03:0002 2017, 02:01:15
3

SRP трудно понять. Это главным образом вопрос назначения «рабочих мест» для вашего кода и обеспечения четкой ответственности каждой части. Как и в реальной жизни, в некоторых случаях расщепление работы среди людей может быть вполне естественным, но в других случаях это может быть очень сложно, особенно если вы не знаете их (или работу).

Я всегда рекомендую вам просто написать простой код, который работает сначала , а затем немного реорганизовать: вы будете склонны видеть, как кластер кода естественным образом через некоторое время. Я думаю, что это ошибка, чтобы навязать свои обязанности, прежде чем вы знаете код (или людей) и работу, которую нужно выполнить.

Одна вещь, которую вы заметите, - это когда модуль начинает делать слишком много и трудно отлаживать /поддерживать. Это момент для рефакторинга; какова должна быть основная работа и какие задачи могут быть отданы другому модулю? Например, следует ли обрабатывать проверки безопасности и другую работу, или сначала выполнять проверки безопасности в другом месте, или это сделает код более сложным?

Используйте слишком много указаний, и это снова становится беспорядком ... что касается других принципов, это будет противоречить другим, таким как KISS, YAGNI и т. д. Все зависит от баланса.

ответил Christophe Roussy 28 MaramTue, 28 Mar 2017 11:35:47 +03002017-03-28T11:35:47+03:0011 2017, 11:35:47
3

«Принцип единой ответственности», возможно, является запутанным именем. «Только одна причина для изменения» - лучшее описание принципа, но все же легко понять неправильно. Мы не говорим о том, что заставляет объекты изменять состояние во время выполнения. Мы занимаемся тем, что может заставить разработчиков изменить код в будущем.

Если мы не исправим ошибку, это изменение будет связано с новым или измененным требованием к бизнесу. Вам придется думать вне самого кода и представить, какие внешние факторы могут вызывать требования к изменению независимо . Скажи:

  • Налоговые ставки меняются в связи с политическим решением.
  • Маркетинг решает изменить имена всех продуктов
  • Пользовательский интерфейс должен быть изменен для доступа
  • База данных перегружена, поэтому вам нужно сделать некоторые оптимизации
  • Вам необходимо разместить мобильное приложение
  • и т. д.

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

Таким образом, не просто сосредоточиться на том, что может измениться - что-либо может измениться в будущем. Сосредоточьтесь на том, что может самостоятельно изменить. Изменения обычно независимы, если они вызваны разными участниками.

Ваш пример с титрами работы находится на правильном пути, но вы должны взять его более буквально! Если маркетинг может привести к изменению кода и финансам, это может привести к другим изменениям, эти изменения не должны влиять на один и тот же код, поскольку это буквально разные названия должностей, и поэтому изменения будут происходить независимо.

Чтобы процитировать дядю Боба , который изобрел термин:

  

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

Итак, подведите итог: «Ответственность» относится к единой бизнес-функции. Если более чем один актер может заставить вас изменить класс, тогда класс, вероятно, нарушит этот принцип.

ответил JacquesB 28 MarpmTue, 28 Mar 2017 18:04:44 +03002017-03-28T18:04:44+03:0006 2017, 18:04:44
2

Хорошая статья, которая объясняет принципы программирования SOLID и дает примеры кода, следующие и не следуя этим принципам: https://scotch.io/bar-talk/solid-the-first-five-principles-of-object-oriented-design .

В примере, относящемся к SRP, он приводит пример нескольких классов формы (круг и квадрат) и класс, предназначенный для вычисления общей площади нескольких фигур.

В своем первом примере он создает класс вычисления области и возвращает его результат как HTML. Позже он решает, что хочет отобразить его как JSON вместо этого, и ему нужно изменить класс вычисления площади.

Проблема с этим примером заключается в том, что его класс вычисления области отвечает за вычисление области фигур и отображение этой области. Затем он использует лучший способ сделать это, используя другой класс, специально предназначенный для отображения областей.

Это простой пример (и более понятное чтение статьи, так как оно содержит фрагменты кода), но демонстрирует основную идею SRP.

ответил blitz1616 27 MarpmMon, 27 Mar 2017 23:21:35 +03002017-03-27T23:21:35+03:0011 2017, 23:21:35
0

Прежде всего, то, что у вас есть, это на самом деле две проблемы separate : проблема того, какие методы следует вводить в ваши классы, и проблема раздувания интерфейса.

Интерфейсы

У вас есть этот интерфейс:

открытый интерфейс CustomerCRUD
{
  public void Create (Клиент клиента);
  открытый клиент Read (int CustomerID);
  public void Update (Клиент клиента);
  public void Удалить (int CustomerID);
}

Предположительно, у вас есть несколько классов, которые соответствуют интерфейсу CustomerCRUD (в противном случае интерфейс не нужен), а также некоторая функция do_crud (customer: CustomerCRUD), которая принимает соответствующий объект. Но вы уже нарушили SRP: вы связали эти четыре разных операции вместе.

Предположим, что позже вы будете работать с представлениями в базе данных. В представлении базы данных есть только доступный для него метод Read. Но вы хотите написать функцию do_query_stuff (customer: ???), чтобы операторы прозрачно отображались в полноэкранных таблицах или представлениях; он использует метод Read, в конце концов.

Итак, создайте интерфейс

открытый интерфейс CustomerReader   {     открытый клиент Read (customerID: int)   }

и укажите свой CustomerCrud как:

открытый интерфейс CustomerCRUD расширяет CustomerReader
{
  public void Create (Клиент клиента);
  public void Update (Клиент клиента);
  public void Удалить (int CustomerID);
}

Но нет конца. Могут быть объекты, которые мы можем создать, но не обновлять и т. Д. Это отверстие кролика слишком глубокое. Единственный разумный способ придерживаться принципа единой ответственности - сделать все ваши интерфейсы в точности одним методом . Go фактически следует этой методологии из того, что я видел, причем подавляющее большинство интерфейсов содержит одну функцию; если вы хотите указать интерфейс, содержащий две функции, вам неудобно создать новый интерфейс, который объединяет эти два. Вскоре вы получите комбинаторный взрыв интерфейсов.

Выход из этого беспорядка заключается в использовании структурного подтипирования (реализованного, например, OCaml), а не интерфейсов (которые являются формой номинального подтипирования). Мы не определяем интерфейсы; вместо этого мы можем просто написать функцию

let do_customer_stuff customer = customer.read ... customer.update ...

, который вызывает любые методы, которые нам нравятся. OCaml будет использовать вывод типа, чтобы определить, что мы можем передать любой объект, реализующий эти методы. В этом примере было бы определено, что customer имеет тип <read: int -> unit, update: int -> unit, ...>.

Классы

Это решает проблему interface ; но нам еще нужно реализовать классы, которые содержат несколько методов. Например, следует ли создать два разных класса, CustomerReader и CustomerWriter? Что делать, если мы хотим изменить способ чтения таблиц (например, мы теперь кэшируем наши ответы в redis раньше, после получения данных), но теперь, как они написаны? Если вы следуете этой цепочке рассуждений своим логическим завершением, вы можете внести существенный вклад в функциональное программирование:)

ответил gardenhead 28 MaramTue, 28 Mar 2017 01:14:52 +03002017-03-28T01:14:52+03:0001 2017, 01:14:52
0

На мой взгляд, самое близкое к SRP, которое приходит мне на ум, - это поток использования. Если у вас нет четкого потока использования для любого данного класса, возможно, ваш класс имеет запах дизайна.

Поток использования будет определенной последовательностью вызовов метода, которая даст вам ожидаемый результат (таким образом проверяемый). В основном вы определяете класс, в случае использования которого он получил IMHO, поэтому вся программная методология фокусируется на интерфейсах по реализации.

ответил Arthur Havlicek 29 MaramWed, 29 Mar 2017 00:43:20 +03002017-03-29T00:43:20+03:0012 2017, 00:43:20
0

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

Но удача в том, что на первый взгляд, когда вы впервые узнаете о SOLID.


Я вижу много комментариев, говорящих, что SRP и YAGNI могут противоречить друг другу, но YAGN Я применял TDD (GOOS, Лондонская школа) научил меня думать и проектировать мои компоненты с точки зрения клиента. Я начал разрабатывать свои интерфейсы с помощью того, что меньше всего хочет любой клиент, а именно, как мало он должен делать . И это упражнение может быть выполнено без каких-либо знаний о TDD.

Мне нравится техника, описанная дядей Бобом (я не могу вспомнить, откуда, к сожалению), что-то вроде:

  

Спросите себя, что делает этот класс?

     

В вашем ответе был либо символ And , либо Or

     

Если да, извлеките эту часть ответа, это ответственность за свои собственные

Этот метод является абсолютным, и, как сказал @svidgen, SRP является суждением, но, изучая что-то новое, абсолюты являются лучшими, проще просто always что-то делать. Убедитесь, что причина, по которой вы не разделяете; образованная оценка и не , потому что вы не знаете, как это сделать. Это искусство, и оно требует опыта.


Я думаю, что многие ответы, похоже, приводят аргумент в пользу развязки при разговоре о SRP .

SRP не , чтобы убедиться, что изменение не распространяется по графику зависимости.

Теоретически без SRP у вас не будет никаких зависимостей ...

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

Итак, при обучении SOLID в целом будьте осторожны, чтобы научить, что SRP позволяет изменять меньше кода при изменении требований, когда на самом деле он позволяет вам писать менее новый .

ответил Chris Wohlert 29 MarpmWed, 29 Mar 2017 17:29:47 +03002017-03-29T17:29:47+03:0005 2017, 17:29:47
0

Нет четкого ответа на этот вопрос. Хотя вопрос узкий, объяснений нет.

Для меня это что-то вроде Razor Оккама, если вы хотите. Это идеальный вариант, когда я пытаюсь измерить свой текущий код. Трудно прибить его простым и простыми словами. Другой метафорой будет «одна тема», которая является абстрактной, т. Е. Трудно понять, как «единая ответственность». Третий дескриптор будет «иметь дело с одним уровнем абстракции».

Что это значит практически?

В последнее время я использую стиль кодирования, который состоит в основном из двух фаз:

Фаза I лучше всего описывается как творческий хаос. На этом этапе я записываю код, поскольку мысли текут - то есть сырые и уродливые.

Фаза II - полная противоположность. Это похоже на очистку после урагана. Это занимает большую часть работы и дисциплины. И затем я смотрю на код с точки зрения дизайнера.

Сейчас я работаю в основном на Python, что позволяет мне думать об объектах и ​​классах позже. Первая Фаза I - Я пишу только функции и распространяю их почти случайно в разных модулях. В Phase II , после того, как я все понял, я более подробно рассмотрю, какой модуль имеет дело с какой частью решения. И, просматривая модули, темы появляются для меня. Некоторые функции связаны тематически. Это хорошие кандидаты для классов . И после того, как я превратил функции в классы - это почти сделано с отступом и добавлением self в список параметров в python;) - я использую SRP, как Razor Оккама, чтобы отключить функциональность к другим модулям и классам.

В текущем примере может быть запись небольших функций экспорта на другой день.

Была необходимость в csv , excel и объединена excel sheets в zip.

Простая функциональность выполнялась в трех представлениях (= функции). Каждая функция использовала общий метод определения фильтров и второй метод для извлечения данных. Затем в каждой функции подготовка экспорта происходила и доставлялась как ответ от сервера.

Было слишком много уровней абстрагирования:

I), касающийся входящего /исходящего запроса /ответа

II) определение фильтров

III) получение данных

IV) преобразование данных

Простым шагом было использование одной абстракции (exporter) для обработки слоев II-IV на первом шаге.

Осталось только тема , связанная с запросами /ответами . На том же уровне абстракции извлекаются параметры запроса , что хорошо. Поэтому у меня было для этой view одна «ответственность».

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

Определение критериев фильтра и фактического восстановления почти на одном уровне абстракции (фильтры необходимы для получения правильного подмножества данных). Эти уровни были помещены в нечто вроде уровня доступа .

На следующем шаге я разложил механизмы экспорта: где нужно писать временный файл, я сломал это на две «обязанности»: один для фактической записи данных на диск и другую часть, которая касалась фактический формат.

По мере формирования классов и модулей все стало понятнее, что было где. И всегда скрытый вопрос, делает ли класс слишком много .

  

Как вы определяете, какие обязанности должен иметь каждый класс, и как вы определяете ответственность в контексте SRP?

Трудно дать рецепт, чтобы следовать. Конечно, я мог бы повторить критический «один уровень абстракции» - правило, если это помогает.

В основном для меня это своего рода «художественная интуиция», которая ведет к нынешнему дизайну; Я модель кода, как художник может лепить глину или делает живопись.

Представьте меня как Кодирование Bob Ross ;)

ответил Thomas Junk 30 MarpmThu, 30 Mar 2017 22:50:21 +03002017-03-30T22:50:21+03:0010 2017, 22:50:21
0

Что я пытаюсь сделать, чтобы написать код, который следует за SRP:

  • Выберите конкретную проблему, которую необходимо решить;
  • Напишите код, который его решает, напишите все одним способом (например: main);
  • Тщательно проанализируйте код и, основываясь на бизнесе, попытайтесь определить обязанности, которые видны во всех выполняемых операциях (это субъективная часть, которая также зависит от бизнеса /проекта /клиента);
  • Обратите внимание, что все функциональные возможности уже реализованы; следующая - это только организация кода (с этого момента в этом подходе не будет реализована дополнительная функция или механизм);
  • В зависимости от обязанностей, которые вы определили на предыдущих шагах (которые определены на основе бизнеса и идеи «одна причина для изменения»), извлеките отдельный класс или метод для каждого из них;
  • Обратите внимание, что этот подход касается только SPR; в идеале здесь должны быть дополнительные шаги, направленные на то, чтобы придерживаться других принципов.

Пример:

Проблема: получить два числа от пользователя, рассчитать их сумму и вывести результат пользователю:

 //первый шаг: немедленно решить проблему
static void Main (string [] args)
{
    Console.WriteLine («Номер 1:»);
    int firstNumber = Convert.ToInt32 (Console.ReadLine ());

    Console.WriteLine («Номер 2:»);
    int secondNumber = Convert.ToInt32 (Console.ReadLine ());

    int result = firstNumber + secondNumber;

    Console.WriteLine («Привет!» Результат: {0} ", результат);

    Console.ReadLine ();
}

Затем попробуйте определить обязанности на основе задач, которые необходимо выполнить. Извлеките соответствующие классы:

 //Ответственный за получение двух целых чисел от пользователя
class Input {
    public int FirstNumber {get; задавать; }
    public int SecondNumber {get; задавать; }
    public void Read () {
        Console.WriteLine («Номер 1:»);
        FirstNumber = Convert.ToInt32 (Console.ReadLine ());

        Console.WriteLine («Номер 2:»);
        SecondNumber = Convert.ToInt32 (Console.ReadLine ());
    }
}

//Ответственный за вычисление суммы двух целых чисел
класс SumOperation {
    public int Результат {get; задавать; }
    public void Calculate (int a, int b) {
        Результат = a + b;
    }
}

//Ответственный за вывод некоторого значения пользователю
class Output {
    public void Write (int result) {
        Console.WriteLine («Hello! Результат: {0}», результат);
    }
}

Затем реорганизованная программа становится:

 //Программа: отвечает за основное выполнение.
//Получает два числа от пользователя и выводит их сумму.
static void Main (string [] args)
{
    var input = new Input ();
    input.Read ();

    var operation = new SumOperation ();
    operation.Calculate (input.FirstNumber, input.SecondNumber);

    var output = new Output ();
    output.Write (operation.Result);

    Console.ReadLine ();
}

Примечание: этот очень простой пример учитывает только принцип SRP. Использование других принципов (например: «L» -код должно зависеть от абстракций, а не от конкреций) обеспечит больше преимуществ для кода и сделает его более удобным для ведения бизнеса.

ответил Emerson Cardoso 3 +03002017-10-03T22:07:09+03:00312017bEurope/MoscowTue, 03 Oct 2017 22:07:09 +0300 2017, 22:07:09
0

Из книги Роберта К. Мартинса Чистая архитектура: Руководство мастера по структуре и дизайну программного обеспечения , опубликованное 10 сентября 2017 года, Роберт пишет на стр. 62 следующее:

  

Исторически, SRP был описан следующим образом:

     

Модуль должен иметь одну и только одну причину изменения

     

Программные системы изменены для удовлетворения потребностей пользователей и заинтересованных сторон; те   пользователи и заинтересованные стороны являются «причиной изменения». что   принцип говорит. Действительно, мы можем перефразировать принцип   скажите это:

     

Модуль должен отвечать одному и только одному пользователю или заинтересованному лицу

     

К сожалению, слово «пользователь» и «заинтересованная сторона» на самом деле не являются   подходящее слово для использования здесь. Вероятно, будет более одного пользователя или   заинтересованных сторон, которые хотят, чтобы система изменилась разумно. Вместо   мы действительно имеем в виду группу - одного или нескольких людей, которые требуют   это изменение. Мы будем ссылаться на эту группу как актер .

     

Таким образом, окончательная версия SRP:

     

Модуль должен отвечать за одного и только за одного, актера.

Итак, это не о коде. SRP - это контроль потока требований и потребностей бизнеса, которые могут исходить только из одного уровня.

ответил 4rchit3ct 23 J000000Monday18 2018, 23:10:40

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132