Building a (multiple) complex object

When I create classes, simple constructors tend to be the norm. In one of my current projects, the movie library, I have a Movie domain object. It has a number of properties, as a result of which a constructor is created as follows:

 public Movie(string title, int year, Genre genre, int length, IEnumerable<string> actors) { _title = title; _year = year; _genre = genre; _length = length; _actors = new List<string>(actors); } 

This is not scary, but it is also not easy. Would it be wise to use the factory ( static Movie CreateMovie(...) ) method, or perhaps a builder? Is there a typical template for instantiating domain classes?

UPDATE: thanks for the answers. I probably first changed my mind about this issue, although I learned a few things that would be useful in more difficult situations. My solution now is for the header to be the only required parameter and the rest to be named / optional parameters. This seems like the perfect way to build this domain object.

+6
constructor c # dns
source share
9 answers

If you are using .NET 4.0, you can use optional / named parameters to make it easier to create an object that takes several arguments, some of which are optional. This is useful when you want to avoid many different overloads to provide the necessary information about the object.

If you are not using .NET 4, you can use the Object Builder template to build your type. The object builder takes a little effort to implement and synchronize with your type - so if that's enough, it depends on your situation.

I believe that the linker pattern is most effective at assembling a hierarchy, not a type with many properties. In the latter case, I usually either overload or optional / named parameters.

+4
source share

Yes, using the factory method is a typical pattern, but the question arises: why do you need it? This is what Wikipedia says about factory Methods:

Like other creation templates, he considers the problem of creating objects (products) without specifying the exact class of the object to be created. The factory design pattern handles this problem by defining a separate method for creating objects that can then override subclasses to indicate the derived type of product to be created.

So, the factory method template makes sense if you want to return subclasses of Movie . If this is not (and will not) be a requirement, replacing the public constructor with the factory method really serves no purpose.

For the requirements outlined in your question, your solution looks very good to me: all required fields are passed as parameters to the constructor. If none of your fields is required, you can add a default initializer and use the C # object initializer syntax .

+4
source share

It depends.

If this is the only constructor for this class, this means that all properties are needed to instantiate the object. If it matches your business rules, great. If not, it can be a little cumbersome. If, for example, you wanted to sow your system with the help of films, but you didn’t always have actors, you could end up in a pickle.

The specified CreateMovie () method is another option if you need to separate the internal constructor from the action to create the Movie instance.

You have many options for organizing designers. Use the ones that let you design your system without smells and lots of principles (DRY, YAGNI, SRP.)

+3
source share

You gave a good answer to your question, this is a factory template. With the factory template, you do not need huge constructors for encapsulation, you can set the members of the object in your factory function and return this object.

+1
source share

This is quite acceptable, IMHO. I know that static methods are sometimes frowned upon, but I usually throw this code into a static method that returns an instance of the class. I usually do this only for objects that are allowed to have null values.

If the values ​​of the object cannot be empty, add them as parameters to the constructor so that you do not fall into invalid objects.

+1
source share

I don’t see anything bad in your constructor interface and I don’t see what the static method will deliver to you. I will have the same parameters, right?

Parameters do not seem optional, so there is no way to provide overload with less or use optional parameters.

From the perspective of the caller, it looks something like this:

  Movie m = new Movie("Inception", 2010, Genre.Drama, 150, actors); 

The goal of the factory is to provide you with a custom concrete instance of the interface, and not just call the constructor for you. The idea is that the exact class is not hardcoded at the construction site. Is it really better?

  Movie m = Movie.Create("Inception", 2010, Genre.Drama, 150, actors); 

It seems to me almost the same. The only thing that is better if Create() returns other concrete classes than Movie .

One thing to think about is to improve this so that the calling code is easy to understand. The most obvious problem for me is that it's not obvious what 150 means without looking at the code for Movie . There are several ways to improve this if you want:

With a smooth interface, your call will look like

  Movie m = new Movie("Inception"). MadeIn(2010). InGenre(Genre.Drama). WithRuntimeLength(150). WithActors(actors); 

Honestly, all this seems unnecessary for your business. Named parameters are reasonable if you are using .NET 4.0 because they are not much larger than the code and will improve the code of the caller.

+1
source share

I see nothing wrong with leaving the public constructor as it is. Here are some of the rules that I follow when deciding whether to use the factory method.

  • Use the factory method when a complex algorithm is required for initialization.
  • Use the factory method when an IO binding operation is required for initialization.
  • Use the factory method when initialization can throw an exception that cannot be protected at design time.
  • Use the factory method when an extra argument may be required for extra readability.

So, based on my personal rules, I would leave the constructor as it is.

+1
source share

If you can distinguish master data from configuration parameters, create a constructor that accepts all basic data elements and nothing else (even configuration parameters with default values ​​are read-only). Initialize the configuration parameters for normal defaults (in the body of the method) and provide the setters. At this point, the factory method may buy you something if there are general configurations of your object that you want.

Even better, if you find that you have an object that accepts a huge list of parameters, the object may be too thick. You felt that your code could be reorganized. Consider decomposing your object. Good OO literature strongly argues for small objects (e.g. Martin Fowler, Refactoring, Bob Martin, Clean Code). Fowler explains how to decompose large objects. For example, configuration parameters (if any) may indicate the need for more polymorphism, especially if they are logical or enumerable (refactoring "Convert conditional to polymorphism").

I will need to see how your object is used before giving more specific advice. Fowler says that variables that are used together must be turned into their own object. So, for the sake of illustration, if you are calculating certain things based on the genre, year and length, but not other attributes, these together may need to be broken down into your own object, reducing the number of parameters that should be passed to your constructor.

+1
source share

For me, it all depends on your domain model. If your domain model allows you to create simple objects, you must do it.

But often we have many composite objects, and creating each individually is too complex. That is why we are looking for the best way to encapsulate the logic of creating composite objects. In fact, we have only two alternatives described above - “Factory Method” and “Object Builder”. Creating an object through a static method looks a bit strange, because we put the logic of creating an object in an object. Object Builder, in turn, looks complicated.

I think the answer lies in unit tests. This is exactly the case when TDD will be very useful - we take step by step our domain model and understand the need for the complexity of the domain model.

0
source share

All Articles