Поздний ответ, но я не могу удержаться.
Является ли X большинством классов в Y хорошим или антишаблоном?
В большинстве случаев большинство правил, применяемых без размышления, в основном идут ужасно неправильно (включая это).
Позвольте мне рассказать вам историю о рождении объекта на фоне хаоса некоего правильного, быстрого и грязного процедурного кода, который произошел не по замыслу, а из отчаяния.
Мой стажер и я занимаемся парным программированием, чтобы быстро создать какой-то одноразовый код для очистки веб-страницы. У нас нет абсолютно никаких оснований ожидать, что этот код будет жить долго, поэтому мы просто демонстрируем то, что работает. Мы берем всю страницу в виде строки и отбираем все, что нам нужно, самым удивительным образом. Не суди. Оно работает.
Теперь, делая это, я создал несколько статических методов для измельчения. Мой стажер создал класс DTO, очень похожий на ваш CatData
.
Когда я впервые посмотрел на DTO, это меня подкосило. Годы ущерба, нанесенного Java моему мозгу, заставили меня отшатнуться в общественных местах. Но мы работали в C #. C # не нуждается в преждевременных методах получения и установки, чтобы сохранить ваше право делать данные неизменяемыми или инкапсулированными позже. Без изменения интерфейса вы можете добавлять их в любое время. Может быть, просто так вы можете установить точку останова. Все, не говоря своим клиентам об этом. Да, C #. Бу Ява.
Так что я держал язык за зубами. Я наблюдал, как он использовал мои статические методы для инициализации этой вещи перед ее использованием. У нас было около 14 из них. Это было ужасно, но у нас не было причин для беспокойства.
Тогда нам это было нужно в других местах. Мы обнаружили, что хотим скопировать и вставить код. 14 строк инициализации швыряются вокруг. Становилось больно. Он колебался и спросил меня об идеях.
Я неохотно спросил: "Вы считаете объект?"
Он оглянулся на своего DTO и смутил лицо в замешательстве. «Это объект».
«Я имею в виду реальный объект»
«А?»
«Позвольте мне показать вам кое-что. Вы решаете, полезно ли это»
Я выбрал новое имя и быстро набрал что-то похожее на это:
public class Cat{
CatData(string catPage) {
this.catPage = catPage
}
private readonly string catPage;
public string name() { return chop("name prefix", "name suffix"); }
public string weight() { return chop("weight prefix", "weight suffix"); }
public string image() { return chop("image prefix", "image suffix"); }
private string chop(string prefix, string suffix) {
int start = catPage.indexOf(prefix) + prefix.Length;
int end = catPage.indexOf(suffix);
int length = end - start;
return catPage.Substring(start, length);
}
}
Это ничего не делало, статические методы еще не делали. Но теперь я поместил 14 статических методов в класс, где они могли бы быть наедине с данными, с которыми они работали.
Я не заставлял своего интерна использовать это. Я просто предложил это и позволил ему решить, хочет ли он придерживаться статических методов. Я пошел домой, думая, что он, вероятно, будет придерживаться того, что у него уже было. На следующий день я обнаружил, что он использовал его в нескольких местах. Он расшифровал остальную часть кода, который все еще был уродливым и процедурным, но теперь эта сложность была скрыта от нас за объектом. Это было немного лучше.
Теперь убедитесь, что каждый раз, когда вы получаете доступ к этому, он выполняет большую часть работы. DTO - хорошее быстрое кешируемое значение. Я беспокоился об этом, но понял, что могу добавить кеширование, если нам когда-нибудь понадобится, не касаясь какого-либо кода. Так что я не собираюсь беспокоиться, пока мы не заботимся.
Я говорю, что вы всегда должны придерживаться ОО-объектов, а не ОТО? Нет. DTO сияет, когда вам нужно пересечь границу, которая удерживает вас от движущихся методов. У DTO есть свое место.
Но так же и объекты OO. Узнайте, как использовать оба инструмента. Узнайте, что стоит каждый. Научитесь решать проблему, ситуацию и стажера. Догма не твой друг здесь.
Поскольку мой ответ уже смехотворно длинен, позвольте мне разубедить вас в некоторых заблуждениях при рассмотрении вашего кода.
Например, класс обычно имеет члены класса и методы, например:
public class Cat{
private String name;
private int weight;
private Image image;
public void printInfo(){
System.out.println("Name:"+this.name+",weight:"+this.weight);
}
public void draw(){
//some draw code which uses this.image
}
}
Где твой конструктор? Это не показывает мне достаточно, чтобы знать, полезно ли это.
Но после прочтения принципа единой ответственности и открытого закрытого принципа, я предпочитаю разделить класс на DTO и вспомогательный класс только статическими методами, например:
public class CatData{
public String name;
public int weight;
public Image image;
}
public class CatMethods{
public static void printInfo(Cat cat){
System.out.println("Name:"+cat.name+",weight:"+cat.weight);
}
public static void draw(Cat cat){
//some draw code which uses cat.image
}
}
Я думаю, что это соответствует принципу Единой ответственности, потому что теперь ответственность CatData состоит в том, чтобы хранить только данные, не заботясь о методах (также для CatMethods).
Вы можете делать много глупостей во имя принципа единой ответственности. Я могу утверждать, что строки Cat и строки Cat должны быть разделены. Это методы рисования и изображения должны иметь свой собственный класс. То, что ваша работающая программа - это единственная ответственность, поэтому у вас должен быть только один класс. :П
Для меня лучший способ следовать принципу единой ответственности - найти хорошую абстракцию, которая позволит вам поместить сложность в коробку, чтобы вы могли ее скрыть. Если вы можете дать ему хорошее имя, которое удерживает людей от удивления тем, что они находят, когда они заглядывают внутрь, вы довольно хорошо следовали этому. Ожидать, что это будет диктовать больше решений, чем проблем. Честно говоря, оба ваших кодовых списка делают это, поэтому я не понимаю, почему здесь важен SRP.
И это также соответствует принципу открытого закрытого типа, потому что добавление новых методов не должно изменять класс CatData.
Ну нет. Принцип открытого закрытия не связан с добавлением новых методов. Речь идет о возможности изменить реализацию старых методов и необходимости ничего не редактировать. Ничего, что использует тебя, а не твои старые методы. Вместо этого вы пишете новый код где-то еще. Некоторая форма полиморфизма сделает это хорошо. Не вижу этого здесь.
Мой вопрос, это хорошо или анти-шаблон?
Ну, черт возьми, откуда мне знать? Смотри, в любом случае это имеет свои преимущества и издержки. Когда вы отделяете код от данных, вы можете изменить любой из них без необходимости перекомпилировать другой. Может быть, это очень важно для вас. Может быть, это просто делает ваш код бессмысленно сложным.
Если это заставляет вас чувствовать себя лучше, вы не так уж далеки от того, что Мартин Фаулер называет объектом параметров . Вам не нужно принимать только примитивы в свой объект.
То, что я хотел бы, чтобы вы сделали, - это выработайте понимание того, как выполнять разделение, или нет, в любом стиле кодирования. Потому что, хотите верьте, хотите нет, вам не нужно выбирать стиль. Вы просто должны жить с вашим выбором.