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?

9
  • How to deal with constructors in large data classes
    – gnat
    Commented Dec 17, 2015 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
    Commented Dec 17, 2015 at 12:59
  • Have a look at builder and prototype. Commented Dec 17, 2015 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
    Commented Dec 17, 2015 at 13:23
  • 1
    If your class is too big, shifting around constructor arguments isn't going to make it not too big.
    – Telastyn
    Commented Dec 18, 2015 at 18:33

2 Answers 2

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

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.
2
  • "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
    Commented Dec 24, 2015 at 7: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
    Commented Dec 25, 2015 at 17:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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