OOP model typical errors/ru

From JazzTeamWiki
Jump to: navigation, search

Непродуманная бизнес-логика при создании методов

Предполагается создание модели "Университет". Требуется реализовать use case: "Абитуриент поступает в университет". Для реализации создается класс "Абитуриент", а также методы - "Сдать экзамен", "Выбрать факультет", "Заполнить заявление", "Подать заявление".
Use case(Enter university)
Далее приводится сигнатура метода "Сдать экзамен":
public Mark passExam(Subject subject){
  return new Mark(10);
 }

Как видно, этот метод возвращает Mark - оценка, но это не верно, поскольку "Абитуриент" не может сам себе выставить оценку, для этого нужно создавать дополнительную сущность ("Приемная комиссия" , "Преподаватель", "Компьютер"), которая будет проверять работу абитуриента и выставлять оценку. У "Абитуриента" может быть список оценок, но ключевым моментом является то, что объект "Mark" не может впервые появиться в системе в классе "Абитуриент", этим должна заниматься сущность, которая будет выставлять оценки (создавать объекты "Mark")

Неправильные имена интерфейсов

Модель "Университет". Создаются объекты Abiturient, Student, Teacher. Каждый из них может обучаться(абитуриент сдает вступительные экзамены, студент - экзамены в вузе, преподаватель может обучаться на курсах повышения квалификации и т.д.) Предположим, для реализации этой идеи решено ввести следующий интерфейс :
Class diagramm(wrong Interface naming)

public interface IEducationSystem{
  public void passExam(Subject subject);
}

Называть интерфейс таким именем будет ошибочно, имя интерфейса должно "наделять" класс определенными способностями, свойствами, которые класс реализовывает. А как обычный студент может реализовать целую систему образования(EducationSystem)? В данном случае правильным вариантом названия будет, к примеру, IMemberOfEducationSystem.

Использование агрегации, вместо ассоциации

Агрегация — объекты одного класса входят в объекты другого. Например Колесо в Автомобиль.

Ассоциация означает, что объекты двух классов могут ссылаться один на другой, иметь некоторую связь между друг другом. Например Преподаватель и Студент.

Ошибка заключается том, что ассоциацию, легко перепутать с агрегацией! Допустим есть два класса - база днс и системный администратор. Между этими объектами идет взаимодействие. Системный администратор может добавить новые домены или удалить их из базы:

public class SysAdmins {
   private DataBasesDns basesDns=new DataBasesDns();

    public void addDomain(String domain, String ip) {
        basesDns.setDomain(domain, ip);
    }

    public void removeDomain(String domain) {
        basesDns.removeDomain(domain);
    }

}

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

private DataBasesDns basesDns=new DataBasesDns();

Получается, чтобы обратиться к определенному домену, нужно помнить администратора, в котором есть это база данных и нужный домен. Это не правильно! Нужно отдельно создать объект на класс база данных днс и передавать ее администратору :

public class SysAdmins {
    private DataBasesDns basesDns;

    public SysAdmins(DataBasesDns basesDns) {
        this.basesDns = basesDns;
    }

    public void addDomain(String domain, String ip) {
        basesDns.setDomain(domain, ip);
    }

    public void removeDomain(String domain) {
        basesDns.removeDomain(domain);
    }
}

Здесь видно, что объект база днс передается в конструкторе, и системный администратор с ней работает. Но системный администратор может управлять несколькими базами днс, поэтому стоит добавить еще один метод setDataBasesDns:

    public void setBasesDns(DataBasesDns dns) {
        basesDns = dns;
    }

Советую начинающим ознакомится с этой статьей "Отношения классов — от UML к коду" http://habrahabr.ru/post/150041/

Еще один пример: Здесь для каждого заказа создается новый менеджер. Это неправильно!

    	public void sdelatZakaz(int kolAvto) {
		Manager manager = Manager();
		order = new Order();
		order.setCustomer(this);
		order.setAvto(viborAuto());
		order.setKolAvto(kolAvto);
		manager.oformitArendu(order);
	}

Здесь уже мы видим, что появился метод toOrderCarRental(), который обращаясь к авто парку вернет менеджера.

    public void toOrderCarRental(int numberOfCars) {
		manager = callManager();
		order = new Order();
		order.setCustomer(this);
		order.setAuto(choiceOfCar());
		order.setNumberOfCars(numberOfCars);
		manager.anOrderForTheRental(order);
	}

	private Manager callManager() {
		manager = this.autoPark.getManager().get(0);
		return manager;
	}

Избыточные данные в классах

Так же при построении диаграммы классов необходимо продумывать все поля, которые будет содержать класс, и его взаимодействие с другими классами. Это необходимо делать для того, чтоб при дальнейшей разработке и расширении программы она не ломалась, и ей хватало данных.
Вот пример, который показывает какие поля должен содержать класс:
У класса Customer есть поля: String fio,List<Car> car, int numberOfCars;

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

Вот пример который показывает как должны взаимодействовать классы:
Пример, который показывает, как должны взаимодействовать классы: Есть класс Driver и класс Mechanic. Когда машина ломается, водитель передает её механику, но тут есть одно но: механиков много и кому передать не известно. Выходов из этой ситуации может быть много, я приведу лишь 2:

  • Можно ввести дежурного механика, который осматривает поломку и передает ее свободному механику, а тот её уже чинит.
  • А можно сделать, чтоб водитель сам искал свободного механика и передавал ему машину на ремонт.

Ссылки на полезные статьи

Статья по Code Style

Оживление ООП модели