10

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?

  • 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
  • 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
10

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

4

Do NOT use the second approach.

It is definitely not the solution and would only lead to instantiating classes in your business logic, instead of the part of your app where the factories are.

Either:

  • try to group certain parameters which seem to represent similar things into objects
  • split the current class into several smaller classes (having a service class with 10 parameters seems like the class does too many things)
  • leave it as is, if your class is not actually a service, but a value object instead

Unless the object you are creating is actually a class which is responsible of holding data, you should try to refactor the code and split the large class into smaller ones.

  • Well, the bsiness logic is using a factory which returns the products, thus, the business logic does not see the JSON/ptree stuff. But I see your point, having Parser code in the construcotr feels wrong. – lugge86 Dec 17 '15 at 13:20
  • The class is representing a widget in a GUI for an embedded system, thus, 5+ attributes seems OK for me: x_coord, y_coord, backgroundcolor, framesize, framecolor, text... – lugge86 Dec 17 '15 at 13:21
  • 1
    @lugge86 Even though you are using a factory to parse the JSON, and thus avoiding calling new or constructing objects inside your business logic, it is not very good design. Check the Don't look for things talk by Miško Hevery, who explains in more depth why the factory approach you hinted is bad from both testing and reading point of view. Also, your class seems to be a data object, and for those it is generally fine to have more parameters than the regular service class. I would not be too bothered. – Andy Dec 17 '15 at 13:25
  • I'm feeling fine with my factory approach, but I will follow your link and think about it. However, the factory is not in question in this topic. The question is still how to set up the products... – lugge86 Dec 17 '15 at 15:02
  • "having a service class with 10 parameters seems like the class does too many things" Not in machine learning. Any ML algorithm would have tons of tunable parameters. I wonder what is the right way to deal with that when coding ML. – Siyuan Ren Dec 19 '15 at 9:23
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.
  • "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 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 Dec 25 '15 at 17:14

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.