14

So I have a factory which creates objects of different classes. The possible classes are all derived from an abstract ancestor. The factory has a configuration file (JSON syntax) and decides which class to create, depending on the user's configuration.

To achieve this, the factory uses boost::property_tree for JSON-parsing. He walks through the ptree and decides which concrete object to create.

However, the product-objects have many fields (attributes). Depending on the concrete class, the object has about 5-10 attributes, in the future maybe even more.

So I'm not sure how the constructor of the objects should look like. I can think of two solutions:

1) The product's constructor expects every attribute as a parameter, thus, the constructor will end up with 10+ parameters. This will be ugly and lead to long, unreadable code lines. However, advantage is that the factory can parse the JSON and invoke the constructor with the correct parameters. The product class does not need to know that it has been created due to JSON configuration. It does not need to know there is JSON or configuration involved at all.

2) The product's constructor only expects one argument, the property_tree object. Then it can parse the needed information. If am information in the config is missing or out-of-bounds, each product class can react properly. The factory does not need to know what arguments are needed by the several products. The factory also does not need to know how to react in case of wrong configuration. And the constructor interface is unified and small. But, as a disadvantage, the product needs to extract the needed information from the JSON, thus, it knows how it is constructed.

I tend to prefer solution 2). However, I'm not sure if this is good factory pattern. It feels somehow wrong letting the product know that it is created with JSON configuration. On the other side, new products can be introduced very simple.

Any opinions on that?

gnat
  • 21,442
  • 29
  • 112
  • 288
lugge86
  • 435
  • 1
  • 4
  • 12
  • [How to deal with constructors in large data classes](http://programmers.stackexchange.com/questions/302128/how-to-deal-with-constructors-in-large-data-classes) – gnat Dec 17 '15 at 12:43
  • 1
    I followed your link. There's an example in the top-rated answer from ratchet freak. But what issue does this "builder"-approach solve? There's the code-line DataClass data = builder.createResult();. But the createResults()-method still has to get the 10 parameters into the DataClass object. But How? It seems you just have one more layer of abstraction, but the DataClass' constructor doesn't get smaller. – lugge86 Dec 17 '15 at 12:59
  • Have a look at builder and prototype. – Silviu Burcea Dec 17 '15 at 13:06
  • Silviu Burcea, I did. However, when using builder, how does the builder get the parameters into the product? Somewhere there HAS TO BE a fat interface. The builder is just one more layer, but somehow the parameters have to find their way into the product class. – lugge86 Dec 17 '15 at 13:23
  • Another thing about the builder came to me mind: I have different kind of objects (thats why I use a factory for creating them, you remember). So I need a builder class for ever product class. That seems somehow messy. – lugge86 Dec 17 '15 at 14:58
  • gnat, dropping a link without any comment is no help. Not responding to new questions (due to the uncommented link) makes it even worse. – lugge86 Dec 18 '15 at 11:02
  • 1
    If your class is too big, shifting around constructor arguments isn't going to make it _not too big_. – Telastyn Dec 18 '15 at 18:33
  • @lugge86 note that in C++, your Builder could be a friend class, and can therefore circumvent the need to pass the data via the constructor, but directly manipulate the object's fields instead. This creates a direct coupling, but that's not a huge problem for two classes that are so intimately connected anyway. – Jules Dec 21 '15 at 16:26
  • "_It feels somehow wrong letting the product know that it is created with JSON configuration_" You can always pass a function object that is able to retrieve the c'tor parameters, such that you can vary the data source at the call site. – D Drmmr Jul 21 '16 at 09:44

2 Answers2

14

I wouldn't do option 2, because then you have forever convolved your object's construction with boost property tree parsing. If you're comfortable with a class that needs that many parameters, you should be comfortable with a constructor that needs that many parameters, such is life!

If your main concern is code readability, you can use the builder pattern, it's basically the c++/java stopgap for lack of named arguments. You end up with things that look like this:

MyObject o = MyObject::Builder()
               .setParam1(val1)
               .setParam2(val2)
               .setParam3(val3)
             .build();

So now MyObject will have a private constructor, that gets called in Builder::build. The nice thing is that that will be the only place you ever have to call a constructor with 10 parameters. The boost property tree factory will use the builder, and subsequently if you want to construct a MyObject directly or from a different source, you would go through the builder. And the builder basically lets you clearly name each parameter as you pass it in, so it's more readable. This obviously adds some boilerplate, so you'll have to decide if it's worth it compared to just calling the messy constructor, or lumping together some of your existing parameters into structs, etc. Just throwing another option on the table.

https://en.wikipedia.org/wiki/Builder_pattern#C.2B.2B_Example

Nir Friedman
  • 1,427
  • 9
  • 11
0

Option 2 is almost right.

An improved option 2

Create a "front facing" class who's job it is to take that JSON-structure object and pick out the bits and call the factory constructor(s). It takes what the factory makes and gives it to the client.

  • The factory has absolutely no idea that such a JSON thingy even exists.
  • The client does not have to know what specific bits the factory needs.

Basically the "front end" is saying to the 2 Bobs: "I deal with the redacted customers so the engineers don't have to! I have people skills!" Poor Tom. If he'd only said "I decouple the client from the construction. This result is a highly cohesive factory"; he might have kept his job.

Too Many Arguments?

Not for the client - front end communication.

Front end - factory? If not 10 parameters then the best you can do is put off unpacking, if not the original JSON thing then some DTO. Is this better than passing the JSON to the factory? Same difference I say.

I would strongly consider passing individual parameters. Stick to the goal of a clean, cohesive factory. Avoid the concerns of @DavidPacker answer.

Mitigating "too many arguments"

  • Factory or Class constructors

    • taking only arguments for specific class/object construction.
    • default parameters
    • optional parameters
    • named arguments
  • Front end argument grouping

    • Examines, evaluates, validates, sets, etc. argument values guided by the constructor signatures above.
radarbob
  • 5,808
  • 18
  • 31
  • "The factory has absolutely no idea that such a JSON thingy even exists" - well, then what is the factory for?? It hides the details of product creation from both, product and consumer. Why should yet another class help? I'm fine with the factory speaking JSON. One can implement another factory for parsing XML and implement "Abstract Factory" in the future... – lugge86 Dec 24 '15 at 07:50
  • Mr. Factory: "What object do you want? ... What's that? Just tell me what class-object to build." The configuration file JSON is a data source, as Uncle Bob says "it's an implementation detail." It could from another source and/or in another form. Generally we want to decouple from the specific datasource details. Should the source or form change, the factory will not. Given a source+parser, and a factory as decoupled modules makes both reusable. – radarbob Dec 25 '15 at 17:14