В 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 освободит память, используемую этим объектом, и выделит другую.
существует много очевидных случаев, например, CoffeeAndSoupFactory
. Кофе и суп в том же устройстве могут привести к довольно неприятным результатам. В этом примере устройство могло бы быть повреждено в HotWaterGenerator
и некоторый Stirrer
. Тогда новое CoffeeFactory
и SoupFactory
может быть создано из тех компонентов, и любого случайного смешивания можно избежать.
Среди более тонких случаев, сила между объектами доступа к данным (ДАО) и объекты передачи данных (DTOs) очень распространена. ДАО говорят с базой данных, DTOs являются сериализуемыми для передачи между процессами и машинами. Обычно ДАО нужна ссылка на Вашу платформу базы данных, поэтому они неприменимы на Ваших толстых клиентах, которые и при этом драйверы базы данных не установили, ни не имеют необходимые полномочия получить доступ к DB.
методы в классе начинают группироваться областями функциональности ("это эти Coffee
методы, и это эти Soup
методы").
Реализация многих интерфейсов.
Ну, этот принцип должен использоваться с небольшим количеством соли... для предотвращения взрыва класса.
А единственная ответственность не переводит в классы отдельного метода. Это означает единственную причину существования... сервис, что объект предусматривает свои клиенты.
А хороший способ остаться на дороге... Используйте объект в качестве метафоры человека... Если бы объект был человеком, который я попросил бы делать это? Возложите ту ответственность на соответствующий класс. Однако Вы не попросили бы, чтобы тот же человек сделал Ваш управлять файлами, вычислили бы зарплаты, зарплаты проблемы, и проверили бы финансовую документацию... Почему Вы хотели бы, чтобы отдельный объект сделал все они? ( это хорошо, если класс берет несколько обязанностей, пока они все связаны и когерентные. )
Запишите резюме, но детальное описание того, что делает класс.
, Если описание содержит слово "и" затем это должно быть разделено.
Простой и практический метод для проверки единственной ответственности (не только классы, но также и метод классов) является выбором имени. При разработке класса при легком нахождении названия класса, которые определяют точно, что он определяет, Вы находитесь правильным способом. Трудность выбрать имя является рядом всегда признаком плохого дизайна.
методы в Вашем классе должны быть связными..., они должны сотрудничать и использовать те же структуры данных внутренне. Если Вы находите, что у Вас есть слишком много методов, которые не кажутся полностью хорошо связанными или, кажется, воздействуют на разные вещи, то, довольно вероятно, Вы не несете хорошую единственную ответственность.
Часто трудно первоначально найти обязанности, и иногда необходимо использовать класс в нескольких различных контекстах и затем осуществить рефакторинг класс в два класса, поскольку Вы начинаете видеть различия. Иногда Вы находите, что это - потому что Вы смешиваете абстрактное и конкретное понятие вместе. Они имеют тенденцию быть более твердыми видеть, и, снова, использовать в различных контекстах, поможет разъясниться.
Очевидный знак состоит в том, когда Ваш класс заканчивает тем, что был похож Большой Комок грязи , который является действительно противоположностью SRP (единственный принцип ответственности).
В основном, сервисы всего объекта должны быть сфокусированы на выполнении единственной ответственности, имея в виду каждый раз Ваше изменение класса и добавить сервис, который не уважает это, Вы знаете, что "отклоняетесь" от "правильного" пути;)
причина обычно происходит из-за некоторых быстрых исправлений, торопливо добавленных к классу для восстановления некоторых дефектов. Так эти причиной, почему Вы изменяете класс , обычно являются лучшие критерии, чтобы обнаружить, если Вы собираетесь повредить SRP.
Возможно, немного более технический, чем другие запахи:
я также нахожу, что рефакторинг к единственной ответственности труден. К тому времени, когда Вы наконец находите время для него, различные обязанности класса станут переплетенными в клиентском коде, бывшем сложно факторизовать одну вещь, не повреждая другую вещь. Я допустил бы ошибку на стороне "слишком мало", чем "слишком много" я.
Если Вы заканчиваете с MethodA
, который использует MemberA
и MethodB
, который использует MemberB
, и это не часть некоторого параллелизма или схемы управления версиями, Вы могли бы нарушать SRP.
, Если Вы замечаете, что у Вас есть класс, который просто делегирует вызовы к большому количеству других классов, Вы могли бы застрять в аду прокси-класса. Это особенно верно, если Вы заканчиваете тем, что инстанцировали прокси-класса везде, когда Вы могли просто использовать определенные классы непосредственно. Я видел многое из этого. Думайте ProgramNameBL
и ProgramNameDAL
классы вместо использования шаблона Репозитория.
При нахождении проблем, расширяющих функциональность класса, не будучи боящимися, что Вы могли бы закончить тем, что повредили что-то еще, или Вы не можете использовать класс, не изменяя тонны его опций, которые изменяют его запахи поведения как Ваш класс, делающий слишком много.
, Как только я работал с классом прежней версии, который имел метод "ZipAndClean", который, очевидно, архивировал и чистил определенную папку...
Вот некоторые вещи, которые помогают мне выяснить, нарушает ли мой класс SRP:
Martin's Agile Principles, Patterns, and Practices in C# очень помог мне понять SRP. Он определяет SRP как:
Класс должен иметь только одну причину измениться.
Так что же такое изменение в движении?
Ответ Мартина:
.[...] каждая ответственность является осью перемен. (стр. 116)
и далее:
В контексте ПСП мы определяем ответственность как причину изменений. Если вы можете придумать несколько мотивов изменения класса, то этот класс несет более чем одну ответственность (стр. 117)
На самом деле SRP инкапсулирует изменение. Если изменение происходит, оно должно иметь только локальное воздействие.
Где YAGNI?
YAGNI можно хорошо комбинировать с SRP: Когда вы применяете YAGNI, вы ждете, пока на самом деле произойдет некоторое изменение. Если это произойдет, вы должны быть в состоянии четко видеть ответственность, которая вытекает из причины(ов) изменения.
Это также означает, что ответственность может эволюционировать с каждым новым требованием и изменением. Дальнейшее обдумывание SRP и YAGNI даст вам возможность мыслить в гибких конструкциях и архитектурах
.Еще одно эмпирическое правило, которое я хотел бы ввести, следующее:
Если вы чувствуете необходимость либо написать какой-то картезианский продукт из кейсов в ваших тестовых случаях, либо если вы хотите высмеять определенные частные методы класса, то единственная ответственность нарушается.
Недавно я имел это в виду следующим образом: У меня было китообразное абстрактное синтаксическое дерево корутины, которое позже будет сгенерировано на 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 в различных других преобразованиях. :)
.