Вопрос в том, чего мы пытаемся достичь, когда называем объект?
Это может звучать как маловероятный вопрос, поскольку именование объектов не является редким явлением; это то, что мы делаем каждый день. Боб Мартин в своей книге «
Чистый код» утверждает, что имя объекта должно «раскрывать его намерение», что в широком смысле означает, что он должен сказать нам, почему он существует и что он делает.
Например, если вы читаете чей-то код и сталкиваетесь с классом с именем 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
Объектно-ориентированный системный анализ: моделирование мира в данных