Вопрос

У меня есть занятие :Расписание.

public class Schedule {

private int locationNum;
private int cost;
private String costReason; 
private Date weekOfChange;
private Date dayOfChange;
private String changeReason; 

// and all those getters and setters

public Schedule(int locationNum, int cost, String costReason, Date weekOfChange, Date dayOfChange, String changeReason) throws ApplicationException {
//change is all or nothing - all attributes are present or none
if((weekOfChange!=null && dayOfChange!=null && changeReason!=null) || (weekOfChange==null  && dayOfChange == null && changeReason == null))  {
this.weekOfChange = weekOfChange;
this.dayOfChange = dayOfChange;
this.changeReason = changeReason;
}
else { throw new ApplicationException();}
//similary another if block to ensure that if cost is specified 
//then there exists the corresponding reason code for it.
}
}

Пока что мне нравится мой урок по расписанию.Однако я еще не закончил с проверками, мне пришлось бы провести некоторые другие проверки:

  • является ли locationNum допустимым номером хранилища в базе данных.
  • является ли текст changeReason одним из этих 6 различных кодов changeReason в базе данных.
  • и т.д. и т.п....

Обычно я бы не писал их в классе Schedule, потому что, очевидно, я не могу вызывать DAO из этого класса.Итак, у меня был бы бизнес-уровень и какой-то класс validator, принимающий объект типа Schedule и выполняющий последовательно кучу проверок базы данных и собирающий ошибки для отображения / чего угодно.

Теперь, вот мои вопросы:

  1. Если бы вы рассматривали Schedule как POJO и утверждали, что объект не несет ответственности за проверку самого себя - мне пришлось бы переместить весь код в конструкторе в класс validator бизнес-уровня.Но, если бы я должен был это сделать, разве расписание не было бы анемичным?Это то, что они называют нарушением принципа единой ответственности?
  2. Давайте предположим, что я переместил код, который у меня есть в моем конструкторе, в класс бизнес-уровня, так что все виды проверок теперь находятся на моем деловом уровне.Давайте предположим, что кто-то изменил dayOfChange на NULL в моем хранилище данных, и теперь я загружаю объекты из своей базы данных.Теперь, с таким объектом, мое приложение может сломаться, не так ли?потому что я бы написал код, предполагающий, что правила проверки соблюдены.Я предполагаю, что мой вопрос становится запутанным, но суть, которую я пытаюсь донести, заключается в том, что в этом контексте я бы предпочел, чтобы эти проверки выполнялись в конструкторе, чтобы обеспечить целостность данных, скрытых классом Schedule .
  3. Как это делается обычно?Какова наилучшая практика?

Ценю ваше участие в этой дискуссии.

Это было полезно?

Решение

Если у вас действительно есть "все эти добытчики и установщики" затем вам нужно проверить свой класс лучше, чем в конструкторе.Если инварианты вашего класса таковы, что все weekOfChange, dayOfChange и changeReason должны быть равны нулю или не все должны быть not null, ваши установщики быстро переведут ваш класс в недопустимое состояние.Вы имеете в виду иметь сеттеры или вы имеете в виду, что ваш класс должен быть неизменяемым?

Прежде чем беспокоиться о проверке were, следует, возможно, провести анализ вашего класса.Посмотрите на все его допустимые состояния и инварианты для заданных состояний.Тогда вы поймете, должен ли ваш класс быть изменяемым или неизменяемым.Там, где у вас есть взаимозависимые коварианты (например, weekOfChange, dayOfChannge и changeReason), имеет смысл упаковать их в свой собственный класс и использовать composition в классе Schedule.Это поместит поля "правила", касающиеся этих вещей, в одно место и упростит расписание.

То же самое с другими сотрудничающими полями (такими как стоимость и причина затрат).Затем расписание составляется из самоподтверждающихся классов.Если оба они неизменяемы и Расписание также неизменяемо, у вас есть свой собственный гораздо более простой домен.

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

Итак, у вас есть

    class Schedule {
         private Change change;
         private Cost cost;
         private Location location;

        public Schedule(Location location, Cost cost, Change change) {
           this.change = change;
           this.cost = cost;
           this.location = location;
        }
}

И коллобораторы , такие как

public class Change {
    private Date weekOfChange; //shoudln't these two fields be one, a date is a date after all??
    private Date dayOfChange; //shoudln't these two fields be one??
    private String changeReason; 

    public Change(Date weekOfChange Date dayOfChange, String changeReason) {
      this.weekOfChange = weekOfChange;
      ...etc.
    } 
}

Хотя я бы настоятельно посоветовал вам защитить инварианты ваших классов защитным копированием любых изменяемых значений, которые передаются клиентским кодом.

Другие советы

Если бы вы рассматривали Schedule как POJO и утверждали, что объект не является ответственным за проверку самого себя - я должен был бы переместить весь код в конструктор в business класс валидатора слоя.Но, если я были для этого не график анемия?Это то, что они называют нарушением принципа единой ответственности ?

ПОХО это означает только то, что вам не нужно наследоваться от определенного класса или использовать усилитель байтового кода для получения желаемой функциональности, такой как отображение OR.Это НЕ означает, что объекты должны быть анемичными и не обладать никакой функциональностью.

Теперь, с такого рода объектом, мое приложение может сломаться, не так ли?потому что я бы написал код, предполагающий что правила проверки соблюдены.Я предполагаю мой вопрос становится запутанным, но суть, которую я пытаюсь донести, заключается в том, что в этом контексте я бы предпочел, чтобы эти проверки выполнялись в конструкторе, чтобы обеспечить целостность данных, скрытых Классом Schedule .

Кто сказал, что вы не можете вызывать правила, определенные в отдельном классе, из конструктора вашего класса Schedule?Если ему нужен доступ к закрытым полям, вы могли бы сделать их закрытыми для пакета и иметь класс rules в том же пакете - или передать их в качестве параметров правилу.

Как это делается обычно?Какова наилучшая практика?

Перенос правил проверки в отдельные классы должен зависеть от потребностей вашего приложения, а не от неправильно понятых модных словечек.

Хорошо причинами для создания отдельных классов правил могли бы быть:

  • Правил так много и они настолько сложны, что включение их всех в класс Schedule сделало бы его слишком большим.
  • Вы не всегда хотите применять все правила.
  • Правила могут быть повторно использованы для работы с разными классами , которые не обязательно находятся в одной и той же иерархии наследования (т.е.они работают через интерфейс).

Похоже, что существует два различных варианта поведения для Schedule конструктор.Следовательно, должно быть два конструктора.Возможно, даже два класса реализации.

locationNum и changeReason.В настоящее время они слабо типизированы.Вероятно, у них должен быть свой собственный класс.Следуя вашему вопросу, допустимые значения зависят от контекста.На данный момент Schedule у этого нет никакого контекста.Ты мог бы оставить Schedule независимо от контекста, и в этом случае отсутствуют ограничения базы данных на locationNum и changeReason.Идущий в другую сторону, Schedule, locationNum и changeReason может зависеть от контекста, и конструктор должен просто проверить, что все они находятся в одном контексте.Пакет-частный конструктор (действующий как конструктор бедняка friend) может пропустить проверки.

POJO подразумевает (хорошо написанный) объект и, следовательно, поведение.Вопрос, похоже, в том, что это слово используется для обозначения "структуры".Избегайте структур.

Бизнес-уровень, по-видимому, вводит в заблуждение.Похоже, это бизнес-объект, у него должно быть деловое поведение.Однако он может не содержать бизнес-процесса (сервиса).

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

Обычной практикой является наличие анемичных объектов с процедурным кодом.В большинстве случаев это не лучшая практика.

Почему locationNum является int, когда на самом деле вы ссылаетесь на экземпляр другого класса, скажем Location?Если бы вы использовали правильные ссылки на объекты, вы могли бы проверить их в расписании и настройте ORM (если таковой имеется) для обеспечения ссылочной целостности.

чтобы ответить на ваши вопросы :

1) Я не думаю, что ваш класс будет анемичным, на данный момент это скорее будет DTO, я не вижу в этом проблемы.

2) Если ваша проверка выполняется на бизнес-уровне, это не означает, что вы получите недопустимый объект, потому что проверка все равно будет там, обычно ваш процесс проверки каким-то образом жалуется на исключение InvalidDataException или что-то в этом роде и не позволит вам использовать "поврежденный" объект.Если нулевая дата не является приемлемым значением (обычно значение null не может попасть в базу данных ), то уровень business / dao должен перехватить эту ошибку и предотвратить использование объекта вашим приложением.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top