Статьи

Подсказки из классификации: именование часть 2

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

Вопрос в том, чего мы пытаемся достичь, когда называем объект?
Это может звучать как маловероятный вопрос, поскольку именование объектов не является редким явлением; это то, что мы делаем каждый день. Боб Мартин в своей книге «
Чистый код» утверждает, что имя объекта должно «раскрывать его намерение», что в широком смысле означает, что он должен сказать нам, почему он существует и что он делает.

Например, если вы читаете чей-то код и сталкиваетесь с классом с именем A, то, как и я, вы будете полностью сбиты с толку. Что делает класс А, почему он там? Там нет никакого способа вы можете решить это от его имени; его намерение полностью скрыто. Это крайний пример, но я уверен, что вы согласитесь, что есть примеры названий классов, которые звучат правдоподобно, но не все так ясно. В моем последнем блоге я привел пример класса с именем XMLDataProcessor и сказал, что он имеет неправильное имя. Он соответствует правилу существительного ISA в том смысле, что вы можете сказать «XMLDataProcessor», но не раскрывает его намерения: какие данные XML? С какого корма? И слово «процессор» говорит нам, что он что-то делает, но что? И вообще, разве «процессор» — это
слово-ласка?

В
моем последнем блоге Я намекнул, что различные методы обнаружения объектов были задокументированы за эти годы, и что они включают в себя:

  • Классическая Классификация
  • CRC карты
  • Неофициальные английские описания

Идея, что вы можете классифицировать объекты на наборы или типы, не нова. Это происходит от подобных Платону и Аристотелю в классификации растений и животных, используя технику, подобную Двадцати Вопросам
1, и использовалась в течение многих лет многими другими философами. Идея классификации классов заключается в том, что вы используете связанные свойства в качестве критерия «одинаковости» и что эти свойства не должны быть просто измеримыми характеристиками, но также включают нематериальные свойства, такие как наблюдаемое поведение. Шлаер и Меллор
2 использовали эти идеи, чтобы предложить идею о том, что классы обычно подпадают под одну из следующих категорий:

  • Материальные вещи
  • Роли
  • События
  • взаимодействия

… которому вы можете назначить системные артефакты:

  • Tangible Things — учетная запись, книга продаж, данные XML
  • Роли — клиент, менеджер по продажам, сотрудник
  • События — нажата кнопка, получено сообщение XML
  • Взаимодействия — продажа, встреча

Зная, к какой категории принадлежит объект, вы можете выяснить его намерение (почему он существует и что он делает), а зная его намерение, вы можете придумать действительно хорошее имя. Например: Account, Customer, SalesManager и т. Д.

Есть несколько других систем классификации объектов, но я не буду их здесь цитировать, потому что, и это ключевой вопрос, кто когда-либо делал это? За всю свою карьеру я никогда не видел, чтобы кто-то садился и классифицировал их классы — никогда. Итак, является ли классификация объектов нагрузкой интеллектуальной бессмыслицы? Ответ — «НЕТ». Если вы посмотрите на одного из ключевых участников хорошего проектирования программного обеспечения и текущей практики, вы найдете
аббревиатуру
SOLID , и буквально в центре
SOLID вы найдете
Принцип замещения Лискова . По словам Википедии, это означает, что:


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


Ключевая концепция программирования, которая зависит от нашей способности к дискретной классификации объектов.

Я думаю, что принципал замены Лискова лучше всего проиллюстрировать на примере, поэтому я собираюсь быть совершенно банальным и использовать очень упрощенный пример с Dog-extends Pet:


Сначала мне нужно создать класс Pet …

class Pet {

  protected int somePetAmount = 2;

  public Pet() {
    System.out.println("Creating a Pet");
  }

  public void scratch() {
    System.out.println("Pet is scratching");
  }

  public String getName() {
    return "Unknown";
  }

  public void eat() {
    System.out.println("Pet is eating " + somePetAmount);
  }
}

… сопровождаемый Собакой …

public class Dog extends Pet {

  public Dog() {
    System.out.println("Creating a Dog");
    somePetAmount = 10;
  }

  @Override
  public void scratch() {
    System.out.println("Dog is scratching");
  }

  @Override
  public void eat() {
    System.out.println("Dog is eating " + somePetAmount);
  }

  @Override
  public String getName() {
    return "Fido";
  }
}

Суть всего этого, однако, в коде ниже, где я могу поменять местами собак и питомцев:

public void testLiskov() {

    Pet myPet = new Dog();

    System.out.println("My pet is called: " + myPet.getName());

    myPet.scratch();

    Dog myDog = (Dog) myPet; // cast required even though myPet ISA Dog
    myPet = myDog;

    myDog.eat();

    myPet = new Pet();
    System.out.println("My pet is called: " + myPet.getName());

    myPet.scratch();
    myDog.eat();

    if (myPet instanceof Dog)
      System.out.println("myPet is a Dog");
    else
      System.out.println("A Dog ISA Dog ISA Dog"); // This will print out

    if (myPet instanceof Pet)
      System.out.println("My Dog is a Pet"); // This will print out
    else
      System.out.println("A Dog ISA Dog ISA Dog");
  }

… и причина, по которой я могу это сделать, заключается в классификации.
Перефразируя Википедию:


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


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


1 Вегнер, с.1987. Парадигма классификации объектов в направлениях исследований в объектно-ориентированном программировании. MIT Press.

2
Объектно-ориентированный системный анализ: моделирование мира в данных