10

I am reading the book "Learning TypeScript" by Remo Jansen. In one section the author describes how to create a very simple proof-of-concept MVC framework including how to create the Model class and says the following:

A model needs to be provided with the URL of the web service that it consumes. We are going to use a class decorator named ModelSettings to set the URL of the service to be consumed. We could inject the service URL via its constructor, but it is considered a bad practice to inject data (as opposed to a behavior) via a class constructor.

I don't understand that last sentence. In particular, I don't understand what it means to "inject data". It seems to me that in almost all introductions to JavaScript classes using over-simplified examples, data is introduced ("injected"?) into the constructor via its parameters. For example:

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

I certainly think of name as data, not as behaviour, and it is universally included in this sort of example as a constructor parameter, and there is never any mention that this is bad practice. I thus assume I'm misunderstanding something in the above quote, either what is meant by "data" or by "inject" or something else.

Your answers could include explanations of when, where, how and why to use decorators in JavaScript/TypeScript, as I strongly suspect that concept is intimately connected to the understanding I seek. However, more importantly, I want to understand more generally what is meant by injecting data via a class constructor and why that's bad.


To give more context to the above quote, this is the situation: A Model class is created which, in this example, will be used to create stock exchange models, one for NASDAQ and one for NYSE. Each model requires the path of the web service or static data file that will provide the raw data. The book states that a decorator should be used for this information, rather than a constructor parameter, leading to the following:

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

I just haven't been understanding why I should add the service url via the decorator rather than simply as a parameter for the constructor, e.g.

constructor(metiator : IMediator, serviceUrl : string) {...
  • I would suggest you do a quick search on google about dependency injection. This is not the correct forum to ask this question. :) – toskv Jun 2 '16 at 19:59
  • 1
    I will take your response to heart, but I have searched google, and have encountered discussions of dependency injection. Are "dependency injection" and "data injection" referring to the same thing? Further, it has been my impression that "dependency injection" is a "good thing" (or at least an "alternate thing"), whereas the discussion of "data injection" in the quote I provided makes it seem a "bad thing". – Andrew Willems Jun 2 '16 at 20:03
  • Dependency injection and data injection are 2 different things. the 1st is a design principle while the 2nd is a type of attack. If you want a clearer search term try "inversion of control". It is a bit broader but does help paint a clearer picture as well. – toskv Jun 2 '16 at 20:05
  • 1
    "Data injection" attacks are, I believe, a very different animal that what the quoted book's author talks about when he says "inject data". That is one of the reasons I've been frustrated with google searches on this. Even if I need to understand, e.g. SOLID principles better, I don't understand how providing a "name" as a parameter to a "Person" constructor is normal and OK but providing a "serviceUrl" as a parameter to a "Model" constructor is inappropriate, or how it's even different from the "name"/"Person" example. – Andrew Willems Jun 2 '16 at 20:11
  • 7
    I think Remo is mistaken. Parameters are data, no matter what he says. Data that is injected always has a type, and all types in object-oriented languages have behavior of some sort. – Robert Harvey Jun 2 '16 at 21:12
5

I'll give the author the benefit of the doubt and perhaps that is the way things are for Typescript, but otherwise in other environments that's a totally unsubstantiated claim that shouldn't be taken seriously.

Off the top of my head, I can think of a variety of situations where passing data via constructor is good, some that are neutral, but none where it's bad.

If a particular class depends on a particular piece of data in order to be in a valid state and run properly, it makes perfect sense to demand that data in the constructor. A class representing a serial port could take the port name, a file object could require the filename, a drawing canvas requiring its resolution, etc. Unless you pass the data in the constructor, it's possible you could have the object in an invalid state that has to be watched for and checked. Otherwise you can check only at object instantiation and afterwards assume its working for the most part. The authors claim makes that beneficial situation impossible.

Additionally, deciding to forbid passing data in a constructor also makes virtually all immutable objects impossible. Immutable objects have a variety of benefits in many situations, and all of those would be thrown out with the author's policy.

Even if mutable objects are what you want, how is this bad practice:

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

in favor of:

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

Does the author really think the first is bad practice and I should always go with option 2? I think that's crazy talk.

So, since I don't have the book, and wouldn't read it anyway even if I did, I'd view that statement and pretty much any general statement in it at this point with a significant amount of suspicion.

  • Thanks very much for your discussion. What you say "smells right" to me, especially when you give the Rectangle example. I still wonder if the author is making a distinction between data required for the class versus for each instance of the class. However, I don't think the project the book describes really goes into enough depth to clarify that. As a side note, your answer sent me on an initial investigation of object immutability, however much it does or does not relate to my original question, so thanks for that as well! – Andrew Willems Jun 12 '16 at 18:57
0

I think it is depends on the context what kind of model that is being discussed here. I don't have Remo's book, but I guess that the model is a kind of service model, which needs to retrieve the data from a remote web service. If that is the case, being a web service model, it is better to pass all the data required as arguments in the web service's methods, making the service stateless.

Stateless service have several advantages, for example, anyone reading a service method call need not to lookup when the service is constructed to find out the details of the service called. All the details are shown in the arguments being used in the method call (except the remote url).

  • Yes, the model needs to retrieve the data (eventually from a remote web service as you suggest, but in the book this is just a demo, so it's initially just mock data coded directly inline). I'm not understanding your suggestion about passing data as arguments "in the web service's methods". I was asking about differentiating between passing data as parameters (1) for a constructor versus (2) for a decorator. You seem to be suggesting a 3rd option, i.e. passing data as parameters/arguments for a method of the web service. Am I missing your point? – Andrew Willems Jun 12 '16 at 18:46
0

Just guessing.

If I ear 'inject behaviour, not data', I would think in, instead of doing this:

(Sorry for the example in pseudocode):

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

To do this:

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

This way you can change the behaviour of the noise always, make it random, dependent of one inner variable...

I think that it's all about the 'favour composite over inherit' rule. Which is a great rule, I must say.

This DOES NOT MEAN that you cannot 'inject' the name to the 'Person' Object, obviously, because that name is purely business data. But in the example that you give, the web service, the URL is something you need to generate something somehow that connects a service. That somehow is a behaviour: if you inject the URL, you inject the 'data' necessary to build a 'behaviour', so in that case is better to make the behaviour outside and inject it ready to be used: Instead inject an URL inject an usable connection or an usable connectionbuilder.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.