Что значит вводить данные (против поведения) в конструктор класса, и почему это считается плохой практикой?

10

Я читаю книгу «Изучение шрифтов» Ремо Янсена. В одном разделе автор описывает, как создать очень простую платформу MVC для проверки концепции, включая создание Modelкласса, и говорит следующее:

Для модели необходимо указать URL-адрес веб-службы, которую она использует. Мы собираемся использовать декоратор класса с именем ModelSettings, чтобы установить URL-адрес службы, которая будет использоваться. Мы могли бы внедрить URL службы через ее конструктор, но считается плохой практикой вводить данные (в отличие от поведения) через конструктор класса .

Я не понимаю это последнее предложение. В частности, я не понимаю, что значит «вводить данные». Мне кажется, что почти во всех введениях в классы JavaScript с использованием упрощенных примеров данные вводятся («вводятся»?) В конструктор через его параметры. Например:

class Person {
  constructor(name) {
    this.name = name;
  }
}

Я, конечно, считаю nameданные, а не поведение, и они повсеместно включены в этот пример в качестве параметра конструктора, и никогда не упоминается, что это плохая практика. Таким образом, я предполагаю, что неправильно понимаю что-то в приведенной выше цитате, что означает «данные» или «вводить», или что-то еще.

Ваши ответы могут включать объяснения того, когда, где, как и зачем использовать декораторы в JavaScript / TypeScript, так как я сильно подозреваю, что концепция тесно связана с пониманием, которое я ищу. Однако, что еще более важно, я хочу понять в целом, что подразумевается под внедрением данных через конструктор классов и почему это плохо.


Чтобы дать больший контекст вышеприведенной цитате, вот такая ситуация: Modelсоздан класс, который в этом примере будет использоваться для создания моделей фондовых бирж, одна для NASDAQ и одна для NYSE. Для каждой модели требуется путь к веб-службе или файлу статических данных, который будет предоставлять необработанные данные. В книге говорится, что для этой информации должен использоваться декоратор, а не параметр конструктора, что приводит к следующему:

@ModelSettings("./data/nasdaq.json")
class NasdaqModel extends Model implements IModel {
  constructor(metiator : IMediator) {
    super(metiator);
  }
...
}

Я просто не понимаю, почему я должен добавить URL службы через декоратор, а не просто как параметр для конструктора, например

constructor(metiator : IMediator, serviceUrl : string) {...
Эндрю Виллемс
источник
Я бы посоветовал вам сделать быстрый поиск в Google о внедрении зависимостей . Это не правильный форум, чтобы задать этот вопрос. :)
toskv
1
Я приму ваш ответ близко к сердцу, но я искал в Google и столкнулся с обсуждением внедрения зависимости. Означают ли «внедрение зависимостей» и «внедрение данных» одно и то же? Кроме того, у меня сложилось впечатление, что «внедрение зависимостей» - это «хорошая вещь» (или, по крайней мере, «альтернативная вещь»), тогда как обсуждение «внедрения данных» в приведенной мной цитате делает ее «плохой вещью» ,
Внедрение зависимостей и данных - это две разные вещи. 1-й - это принцип дизайна, а 2-й - тип атаки. Если вы хотите более точный поиск, попробуйте «инверсия контроля». Это немного шире, но помогает нарисовать более четкую картину.
toskv
1
Я полагаю, что атаки с использованием «инъекции данных» - это совсем другое животное, о чем говорит автор цитируемой книги, когда говорит «инъекция данных». Это одна из причин, почему я был разочарован поиском в Google по этому вопросу. Даже если мне нужно лучше понять, например, принципы SOLID, я не понимаю, как предоставление «имени» в качестве параметра конструктору «Person» - это нормально и нормально, но предоставление «serviceUrl» в качестве параметра для «Model» конструктор неуместен, или как он отличается от примера "имя" / "персона".
7
Я думаю, что Римо ошибается. Параметры являются данными, независимо от того, что он говорит. Вводимые данные всегда имеют тип, а все типы в объектно-ориентированных языках имеют своего рода поведение.
Роберт Харви

Ответы:

5

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

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

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

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

Даже если изменяемые объекты - это то, что вы хотите, как это плохая практика:

var blah = new Rectangle(x,y,width,height);

в пользу:

var blah = new Rectangle();
blah.X = x;
blah.Y = y;
blah.Width = width;
blah.Height = height;

Неужели автор действительно думает, что первое - это плохая практика, и мне всегда следует выбирать вариант 2? Я думаю, что это сумасшедший разговор.

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

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

Я думаю, что это зависит от контекста, какая модель обсуждается здесь. У меня нет книги Ремо, но я предполагаю, что эта модель является своего рода сервисной моделью, которая должна извлекать данные из удаленного веб-сервиса. В этом случае, будучи моделью веб-службы, лучше передать все необходимые данные в качестве аргументов в методах веб-службы, что делает службу без сохранения состояния.

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

YudhiWidyatama
источник
Да, модель должна извлекать данные (в конечном итоге из удаленного веб-сервиса, как вы предлагаете, но в книге это всего лишь демонстрация, поэтому вначале это просто фиктивные данные, закодированные непосредственно в строке). Я не понимаю вашего предложения о передаче данных в качестве аргументов "в методах веб-сервиса". Я спрашивал о разграничении передачи данных в качестве параметров (1) для конструктора и (2) для декоратора. Похоже, вы предлагаете третий вариант, то есть передачу данных в качестве параметров / аргументов для метода веб-службы. Я скучаю по твоей точке?
Эндрю Виллемс
0

Просто угадай.

Если бы я услышал «вводить поведение, а не данные», я бы подумал, вместо того, чтобы делать это:

(Извините за пример в псевдокоде):

class NoiseMaker{
  String noise;
  NoiseMaker(String noise){
     this.noise = noise;
  }
  void soNoise(){
    writeToOutput(noise)
  }
}

Сделать это:

interface Noise{
  String getAudibleNoise();
}

class PanicYell implements Noise{
   String getAudibleNoise(){
       return generateRandomYell();
   }
   .....
}



class WhiteNoise implements Noise{
   String getAudibleNoise(){
       return generateNoiseAtAllFrequences();
   }
   .....
}

class NoiseMaker{
  Noise noise;
  NoiseMaker(Noise noise){
     this.noise = noise;
  }
  void soNoise(){
    writeToOutput(noise.getAudibleNoise())
  }
}

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

Я думаю, что это все о правиле «составной состав вместо наследования». Это большое правило, я должен сказать.

Это НЕ ОЗНАЧАЕТ, что вы не можете «внедрить» имя в объект «Персона», очевидно, потому что это имя является чисто бизнес-данными. Но в приведенном вами примере, веб-сервисе, URL-адрес - это то, что вам нужно, чтобы сгенерировать что- то , связывающее сервис. Это как-то поведение: если вы вводите URL, вы вводите «данные», необходимые для построения «поведения», поэтому в этом случае лучше сделать поведение снаружи и внедрить его готовым к использованию: вместо этого введите URL-инъекцию полезное соединение или полезное построение соединений.

eddieferetro
источник