Статьи

Закон Деметры в Java — принцип наименьшего знания — пример из реальной жизни

Закон Деметры, также известный как принцип наименьшего знания, является принципом кодирования, который говорит, что модуль не должен знать о внутренних деталях объектов, которыми он управляет. Если код зависит от внутренних деталей конкретного объекта, есть большая вероятность, что он сломается, как только изменится внутренний объект. Поскольку инкапсуляция полностью скрывает внутренние детали объекта и раскрывает только операции, она также утверждает закон Деметры . Многие программисты на Java допускают одну ошибку, которая раскрывает внутренние детали объекта с помощью методов-получателей, и именно здесь вас предупреждает принцип наименьшего уровня знаний. Я впервые узнал об этом принципе, читая одну из обязательных книг по программированию — «Чистый код» Роберта К. Мартина. Помимо многих хороших вещей, которые преподает вам книга, «принцип наименьшего знания» — это один принцип, который я до сих пор помню. Как и многие плохие вещи, вы соблазнитесь нарушить Закон Деметры из-за прекрасной цепочки методов, написанных в свободном стиле. На первый взгляд, это выглядит довольно хорошо, но как только вы думаете о принципе наименьшего количества знаний, вы начинаете видеть реальную картину. В этой статье мы увидим формальное определение закона Деметры и исследуем фрагмент кода, который нарушает этот принцип.

Закон Деметры

Согласно Закону Деметры, метод M объекта O должен вызывать только следующие типы методов:

  1. Методы самого объекта O
  2. Методы объекта передаются в качестве аргумента
  3. Метод объекта, который содержится в переменной экземпляра
  4. Любой Объект, который создан локально в методе М

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class LawOfDelimterDemo {
 
    /**
     * This method shows two violations of "Law of Delimiter" or "Principle of least knowledge".
     */
    public void process(Order o) {
 
        // as per rule 1, this method invocation is fine, because o is a argument of process() method
        Message msg = o.getMessage();
 
        // this method call is a violation, as we are using msg, which we got from Order.
        // We should ask order to normalize message, e.g. "o.normalizeMessage();"
        msg.normalize();
 
        // this is also a violation, instead using temporary variable it uses method chain.
        o.getMessage().normalize();
 
        // this is OK, a constructor call, not a method call.
        Instrument symbol = new Instrument();
 
        // as per rule 4, this method call is OK, because instance of Instrument is created locally.
        symbol.populate();
 
    }
}

Вы можете видеть, что когда мы получаем внутренний класс Order и вызываем метод для этого объекта, мы нарушаем Закон разделителя , потому что теперь этот метод знает о классе Message. С другой стороны, вызов метода для объекта Order — это нормально, поскольку он передается методу в качестве параметра. Это изображение хорошо объясняет, что вам нужно сделать, чтобы следовать Закону Деметры.

Закон Деметры в Java с примером

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

1
2
3
4
5
public class XMLUtils {
   public Country getFirstBookCategoryFromXML(XMLMessage xml) {
       return xml.getXML().getBooks().getBookArrary(0).getBookHeader().getBookCategory();
   }
}

Этот код теперь зависит от множества классов, например
XMLMessage
XML
Книга
BookHeader
BookCategory

Это означает, что эта функция знает о XMLMessage, XML, Book, BookHeader и BookCategory. Он знает, что у XML есть список
Book, которая, в свою очередь, имеет BookHeader, а внутренне имеет BookCategory, содержит много информации. Если какой-либо из промежуточного класса или метода доступа в этом вызове метода изменится, этот код будет поврежден. Этот код сильно связан и хрупок. Гораздо лучше возложить ответственность за поиск внутренних данных на объект, которому он принадлежит. Если присмотреться, мы должны вызывать только метод getXML (), потому что его метод из класса XMLMessage, который передается методу в качестве аргумента. Вместо того, чтобы помещать весь этот код в XMLUtils, следует использовать BookUtils или что-то подобное, которое все еще может следовать Закону Деметры и может возвращать требуемую информацию.