Как Вы определяете Единственную Ответственность?

В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.

При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.

Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».

Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this. Возьмем этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте вашего кода:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference и otherReference оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.

36
задан Péter Török 17 October 2010 в 14:07
поделиться

12 ответов

Единственный Принцип Ответственности

существует много очевидных случаев, например, CoffeeAndSoupFactory. Кофе и суп в том же устройстве могут привести к довольно неприятным результатам. В этом примере устройство могло бы быть повреждено в HotWaterGenerator и некоторый Stirrer. Тогда новое CoffeeFactory и SoupFactory может быть создано из тех компонентов, и любого случайного смешивания можно избежать.

Среди более тонких случаев, сила между объектами доступа к данным (ДАО) и объекты передачи данных (DTOs) очень распространена. ДАО говорят с базой данных, DTOs являются сериализуемыми для передачи между процессами и машинами. Обычно ДАО нужна ссылка на Вашу платформу базы данных, поэтому они неприменимы на Ваших толстых клиентах, которые и при этом драйверы базы данных не установили, ни не имеют необходимые полномочия получить доступ к DB.

Запахи Кода

  • методы в классе начинают группироваться областями функциональности ("это эти Coffee методы, и это эти Soup методы").

  • Реализация многих интерфейсов.

24
ответ дан David Schmitt 27 November 2019 в 05:29
поделиться

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

А единственная ответственность не переводит в классы отдельного метода. Это означает единственную причину существования... сервис, что объект предусматривает свои клиенты.

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

  • при использовании карты CRC это - хорошая тонкая инструкция. При наличии затруднений при получении всех обязанностей того объекта на карте CRC это, вероятно, делает слишком много..., макс. из 7 сделал бы как хороший маркер.
  • Другой запах кода из книги рефакторинга был бы ОГРОМНЫМИ классами. Хирургия ружья была бы другим... внесение изменения в одну область в классе вызывает ошибки в несвязанных областях того же класса...
  • Открытие, что Вы вносите изменения в тот же класс для несвязанных исправлений ошибок снова и снова, является другим признаком, что класс делает слишком много.
11
ответ дан Peter Mortensen 27 November 2019 в 05:29
поделиться

Запишите резюме, но детальное описание того, что делает класс.

, Если описание содержит слово "и" затем это должно быть разделено.

24
ответ дан Peter Mortensen 27 November 2019 в 05:29
поделиться

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

9
ответ дан Pier Luigi 27 November 2019 в 05:29
поделиться

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

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

5
ответ дан Peter Mortensen 27 November 2019 в 05:29
поделиться

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

В основном, сервисы всего объекта должны быть сфокусированы на выполнении единственной ответственности, имея в виду каждый раз Ваше изменение класса и добавить сервис, который не уважает это, Вы знаете, что "отклоняетесь" от "правильного" пути;)

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

5
ответ дан Peter Mortensen 27 November 2019 в 05:29
поделиться

Возможно, немного более технический, чем другие запахи:

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

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

2
ответ дан Joris Timmermans 27 November 2019 в 05:29
поделиться

Если Вы заканчиваете с MethodA, который использует MemberA и MethodB, который использует MemberB, и это не часть некоторого параллелизма или схемы управления версиями, Вы могли бы нарушать SRP.

, Если Вы замечаете, что у Вас есть класс, который просто делегирует вызовы к большому количеству других классов, Вы могли бы застрять в аду прокси-класса. Это особенно верно, если Вы заканчиваете тем, что инстанцировали прокси-класса везде, когда Вы могли просто использовать определенные классы непосредственно. Я видел многое из этого. Думайте ProgramNameBL и ProgramNameDAL классы вместо использования шаблона Репозитория.

2
ответ дан David Schmitt 27 November 2019 в 05:29
поделиться

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

, Как только я работал с классом прежней версии, который имел метод "ZipAndClean", который, очевидно, архивировал и чистил определенную папку...

0
ответ дан wasker 27 November 2019 в 05:29
поделиться

Вот некоторые вещи, которые помогают мне выяснить, нарушает ли мой класс SRP:

  • Заполните комментарии XML-документа к классу. Если вы используете такие слова, как if, и, но, кроме, когда и т. Д., Ваши классы, вероятно, делают слишком много.
  • Если ваш класс является службой домена, он должен содержать глагол в имени. Часто у вас есть такие классы, как "OrderService", которые, вероятно, следует разделить на GetOrderService, SaveOrderService, SubmitOrderService и т. д.
2
ответ дан 27 November 2019 в 05:29
поделиться

Martin's Agile Principles, Patterns, and Practices in C# очень помог мне понять SRP. Он определяет SRP как:

Класс должен иметь только одну причину измениться.

Так что же такое изменение в движении?

Ответ Мартина:

.

[...] каждая ответственность является осью перемен. (стр. 116)

и далее:

В контексте ПСП мы определяем ответственность как причину изменений. Если вы можете придумать несколько мотивов изменения класса, то этот класс несет более чем одну ответственность (стр. 117)

На самом деле SRP инкапсулирует изменение. Если изменение происходит, оно должно иметь только локальное воздействие.

Где YAGNI?

YAGNI можно хорошо комбинировать с SRP: Когда вы применяете YAGNI, вы ждете, пока на самом деле произойдет некоторое изменение. Если это произойдет, вы должны быть в состоянии четко видеть ответственность, которая вытекает из причины(ов) изменения.

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

.
2
ответ дан 27 November 2019 в 05:29
поделиться

Еще одно эмпирическое правило, которое я хотел бы ввести, следующее:

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

Недавно я имел это в виду следующим образом: У меня было китообразное абстрактное синтаксическое дерево корутины, которое позже будет сгенерировано на C. А пока думайте об узлах как о последовательности, итерации и действии. Sequence chains two coroutines, Iteration повторяет coroutine до тех пор, пока пользовательское условие не станет истинным, а Action выполнит определенное пользовательское действие. Кроме того, можно аннотировать Действия и Итерации кодовыми блоками, которые определяют действия и условия для оценки по мере продвижения коруэтки вперед.

Ко всем этим блокам кода необходимо было применить определенное преобразование (для тех, кому было интересно: мне нужно было заменить концептуальные пользовательские переменные на фактические переменные реализации, чтобы предотвратить столкновение переменных. Те, кто знает макросы lisp, могут подумать о генсиме в действии :) ). Таким образом, самое простое, что могло бы сработать - это посетитель, который знает операцию изнутри и просто вызывает их по аннотированному блоку кода Action'а и Iteration при посещении и обходит все узлы синтаксического дерева. Однако в этом случае мне пришлось бы дублировать утверждение "трансформация применена" в моем тестовом коде для visitAction-Метод и visitIteration-Метод. Другими словами, мне пришлось бы проверить тестовые примеры продукта ответственности Traversion (== {обратная итерация, траверсное действие, траверсная последовательность}) x Transformation (ну, кодовый блок трансформирован, который взорвался в итерацию трансформирован, а действие трансформировано). Таким образом, у меня возник соблазн использовать порошок, чтобы удалить преобразование-Метод и заменить его каким-то 'return "I was transformed!'; -Stub.

Однако, согласно эмпирическому правилу, я разделил класс на класс TreeModifier, который содержит инстанцию NodeModifier, предоставляющую методы modifyIteration, modifySequence, modifyCodeblock и так далее. Таким образом, я мог легко протестировать ответственность за обход, вызов NodeModifier и реконструкцию дерева, а также протестировать фактическую модификацию блоков кода отдельно, тем самым устраняя необходимость в тестировании продукта, так как теперь ответственность была разделена (на обход и реконструкцию и на конкретную модификацию).

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

.
1
ответ дан 27 November 2019 в 05:29
поделиться