Co to znaczy wstrzykiwać dane (vs zachowanie) do konstruktora klasy i dlaczego jest to uważane za złą praktykę?


10

Czytam książkę „Learning TypeScript” Remo Jansena. W jednej sekcji autor opisuje, jak stworzyć bardzo prosty framework MVC typu proof-of-concept, w tym jak stworzyć Modelklasę i mówi:

Do modelu należy podać adres URL usługi internetowej, z której korzysta. Użyjemy dekoratora klasy o nazwie ModelSettings, aby ustawić adres URL usługi, która ma być używana. Możemy wstrzyknąć adres URL usługi za pośrednictwem konstruktora, ale uważa się za złą praktykę wstrzykiwanie danych (w przeciwieństwie do zachowania) za pomocą konstruktora klasy .

Nie rozumiem tego ostatniego zdania. W szczególności nie rozumiem, co to znaczy „wstrzykiwać dane”. Wydaje mi się, że w prawie wszystkich wprowadzeniach do klas JavaScript wykorzystujących zbyt uproszczone przykłady dane są wprowadzane („wstrzykiwane”?) Do konstruktora za pomocą jego parametrów. Na przykład:

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

Z pewnością myślę o namedanych, a nie o zachowaniu, i są one powszechnie zawarte w tym przykładzie jako parametr konstruktora, i nigdy nie ma wzmianki, że jest to zła praktyka. Zakładam więc, że nie rozumiem czegoś w powyższym cytacie, co oznacza „dane”, „zastrzyk” lub coś innego.

Twoje odpowiedzi mogą zawierać wyjaśnienia, kiedy, gdzie, jak i dlaczego używać dekoratorów w JavaScript / TypeScript, ponieważ mocno podejrzewam, że ta koncepcja jest ściśle związana ze zrozumieniem, którego szukam. Co ważniejsze, chcę bardziej ogólnie zrozumieć, co oznacza wstrzykiwanie danych za pomocą konstruktora klas i dlaczego jest to złe.


Aby dać więcej kontekstu powyższemu cytatowi, taka jest sytuacja: Modeltworzona jest klasa, która w tym przykładzie zostanie wykorzystana do stworzenia modeli giełdowych, jednej dla NASDAQ i jednej dla NYSE. Każdy model wymaga ścieżki usługi sieciowej lub pliku danych statycznych, który dostarczy surowych danych. Książka stwierdza, że ​​do tych informacji należy użyć dekoratora, a nie parametru konstruktora, co prowadzi do:

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

Po prostu nie rozumiem, dlaczego powinienem dodawać adres usługi za pośrednictwem dekoratora, a nie tylko jako parametr dla konstruktora, np.

constructor(metiator : IMediator, serviceUrl : string) {...

Sugeruję szybkie przeszukanie google na temat wstrzykiwania zależności . To nie jest właściwe forum do zadawania tego pytania. :)
toskv

1
Przyjmuję twoją odpowiedź do serca, ale szukałem google i spotkałem się z dyskusjami na temat zastrzyku zależności. Czy „wstrzykiwanie zależności” i „wstrzykiwanie danych” odnoszą się do tej samej rzeczy? Co więcej, mam wrażenie, że „wstrzykiwanie zależności” jest „dobrą rzeczą” (a przynajmniej „alternatywną rzeczą”), podczas gdy dyskusja na temat „wstrzykiwania danych” w cytowanym przeze mnie cytacie sprawia, że ​​wydaje się „złą rzeczą” .

Wstrzykiwanie zależności i wstrzykiwanie danych to dwie różne rzeczy. 1. to zasada projektowania, a 2. to rodzaj ataku. Jeśli chcesz wyraźniejszego wyszukiwanego hasła, spróbuj „odwrócić kontrolę”. Jest nieco szerszy, ale pomaga też namalować wyraźniejszy obraz.
toskv

1
Uważam, że ataki „wstrzykiwania danych” to zupełnie inne zwierzę niż to, o czym mówi autor cytowanej książki, gdy mówi „wstrzykiwać dane”. To jeden z powodów, dla których denerwowałem się wyszukiwaniami google w tej sprawie. Nawet jeśli muszę lepiej zrozumieć, np. Zasady SOLID, nie rozumiem, w jaki sposób podanie „nazwy” jako parametru do konstruktora „Osoba” jest normalne i OK, ale podanie „serviceUrl” jako parametru dla „Modelu” konstruktor jest nieodpowiedni, a nawet jak różni się od przykładu „imię” / „osoba”.

7
Myślę, że Remo się myli. Parametry danymi, bez względu na to, co mówi. Wstrzykiwane dane zawsze mają swój typ, a wszystkie typy w językach obiektowych mają pewnego rodzaju zachowanie.
Robert Harvey

Odpowiedzi:


5

Dam autorowi wątpliwość i być może tak właśnie wygląda maszynopis, ale w innych środowiskach jest to całkowicie bezpodstawne twierdzenie, którego nie należy brać na poważnie.

Z czubka głowy potrafię wymyślić różne sytuacje, w których przekazywanie danych przez konstruktor jest dobre, niektóre neutralne, ale żadne nie jest złe.

Jeśli konkretna klasa zależy od określonego elementu danych, aby być w prawidłowym stanie i działać poprawnie, sensowne jest żądanie tych danych w konstruktorze. Klasa reprezentująca port szeregowy może przyjąć nazwę portu, obiekt pliku może wymagać nazwy pliku, rysunku obszaru roboczego wymagającego jego rozdzielczości itp. O ile nie przekazujesz danych do konstruktora, możliwe jest, że obiekt może mieć nieprawidłowy stan, który należy obserwować i sprawdzać. W przeciwnym razie możesz sprawdzić tylko przy tworzeniu instancji obiektu, a następnie przyjąć, że działa on w przeważającej części. Autorzy twierdzą, że ta korzystna sytuacja jest niemożliwa.

Dodatkowo decyzja o zakazie przekazywania danych w konstruktorze uniemożliwia praktycznie wszystkie niezmienne obiekty. Niezmienne obiekty mają wiele zalet w wielu sytuacjach, a wszystkie z nich zostałyby wyrzucone zgodnie z polityką autora.

Nawet jeśli zmienne obiekty są tym, czego chcesz, jaka jest ta zła praktyka:

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

na rzecz:

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

Czy autor naprawdę uważa, że ​​pierwszy to zła praktyka i zawsze powinienem wybierać opcję 2? Myślę, że to szalona rozmowa.

Ponieważ więc nie mam tej książki i nie przeczytałbym jej, nawet gdybym ją miał, z dużym podejrzeniem przeglądałbym to stwierdzenie i prawie wszystkie ogólne stwierdzenia w nim zawarte.


Bardzo dziękuję za rozmowę. To, co mówisz, „pachnie dobrze”, zwłaszcza gdy podajesz przykład Prostokąta. Nadal zastanawiam się, czy autor dokonuje rozróżnienia między danymi wymaganymi dla klasy a danymi dla każdej instancji klasy. Jednak nie sądzę, że projekt opisany w książce naprawdę idzie wystarczająco głęboko, aby to wyjaśnić. Na marginesie, twoja odpowiedź przesłała mi wstępne badanie niezmienności obiektu, niezależnie od tego, czy ma ono związek z moim pierwotnym pytaniem, czy nie, więc dziękuję również za to!
Andrew Willems,

0

Myślę, że to zależy od kontekstu, jaki model jest tutaj omawiany. Nie mam książki Remo, ale sądzę, że model jest rodzajem modelu usługi, który musi pobierać dane ze zdalnej usługi internetowej. W takim przypadku, będąc modelem usługi sieciowej, lepiej przekazać wszystkie dane wymagane jako argumenty w metodach usługi sieciowej, co czyni usługę bezstanową.

Usługa bezstanowa ma kilka zalet, na przykład każdy, kto czyta wywołanie metody usługi, nie musi wyszukiwać, gdy usługa jest zbudowana, aby poznać szczegóły wywoływanej usługi. Wszystkie szczegóły są przedstawione w argumentach używanych w wywołaniu metody (oprócz zdalnego adresu URL).


Tak, model musi pobierać dane (ostatecznie ze zdalnej usługi internetowej, jak sugerujesz, ale w książce jest to tylko wersja demonstracyjna, więc początkowo są to tylko fałszywe dane kodowane bezpośrednio w linii). Nie rozumiem twojej sugestii dotyczącej przekazywania danych jako argumentów „w metodach serwisu internetowego”. Pytałem o rozróżnienie między przekazywaniem danych jako parametrami (1) dla konstruktora a (2) dla dekoratora. Wydaje się, że sugerujesz trzecią opcję, tj. Przekazywanie danych jako parametrów / argumentów dla metody usługi internetowej. Czy brakuje mi twojego punktu?
Andrew Willems,

0

Tylko zgaduję.

Gdybym usłyszał „wstrzykiwanie zachowania, a nie danych”, pomyślałbym, zamiast robić to:

(Przepraszamy za przykład w pseudokodzie):

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

Aby to zrobić:

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())
  }
}

W ten sposób możesz zawsze zmienić zachowanie hałasu, ustawić go losowo, zależnie od jednej zmiennej wewnętrznej ...

Myślę, że chodzi tu o zasadę „faworyzuj kompozyt nad dziedziczenie”. To wspaniała zasada, muszę powiedzieć.

NIE OZNACZA to, że nie można „wstrzyknąć” nazwy do obiektu „Osoba”, oczywiście, ponieważ ta nazwa jest wyłącznie danymi biznesowymi. Ale na przykład, że dajesz, usługę internetową, adres URL jest coś trzeba wygenerować coś jakoś , która łączy usługi. To w jakiś sposób jest zachowanie: jeśli wstrzykujesz adres URL, wstrzykujesz „dane” niezbędne do zbudowania „zachowania”, więc w takim przypadku lepiej jest zrobić zachowanie na zewnątrz i wstrzyknąć je gotowe do użycia: Zamiast tego wstrzyknij adres URL użyteczne połączenie lub użyteczny konstruktor połączeń.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.