What are the pros and cons of having static object creation methods over constructors?
class Foo {
private Foo(object arg) { }
public static Foo Create(object arg) {
if (!ValidateParam(arg)) { return null; }
return new Foo(arg);
}
}
Few that I can think of:
Pros:
- Return null instead of throwing an exception (name it
TryCreate
). This can make code more terse and clean on the client side. Clients rarely expect a constructor to fail. - Create different kinds of objects with clear semantics, e.g.
CreatFromName(String name)
andCreateFromCsvLine(String csvLine)
- Can return a cached object if necessary, or a derived implementation.
Cons:
- Less discoverable, more difficult to skim code.
- Some patterns, like serialization or reflection are more difficult (e.g.
Activator<Foo>.CreateInstance()
)
2 Answers 2
The biggest drawback with static 'creators' is probably limiting inheritance. If you or the user of your library derives a class from your Foo
, then Foo::Create()
becomes pretty much useless. All logic defined there will have to be rewritten again in the inherited Create()
.
I'd suggest a compromise: define a constructor with trivial object initialization logic that never fails/throws, then define creator(s) with caching, alternate construction etc. That leaves the possibility of deriving and you benefit from having creators for a given class.
-
3In practice it's not really an issue, because most classes aren't designed to be inherited from (and thus should be sealed/final). If it later turns out you want to open the class up to inheritance, you can always move any initialization logic to a protected constructor.Doval– Doval2016年08月18日 13:17:59 +00:00Commented Aug 18, 2016 at 13:17
Create is a Factory method. When I feel a need for this, I implement the Factory pattern, and define an interface to represent the contract for creating the object as well as an implementing class, which then gets injected where needed. This retains the advantages of moving the creation responsibility out of the class, but avoids (or at least makes more obvious) the limitations of performing object creation outside the class constructor.
Foo x = Foo.TryCreate(); if (x == null) { ... }
). Handling a ctor exception is (Foo x; try { x = new Foo(); } catch (SomeException e) { ... }
). When calling a normal method, I prefer exceptions to error codes, but with object creation,TryCreate
seems cleaner.Name
andCsvLine
types, rather than express requirements via method names. This would allow you to overload Create. Using strings for both could be considered to be "primitive obsession" (assuming you've not made this choice for known performance reasons). Check out Object Calisthenics for a fun way to explore this.