Factories for Returned Types

As discussed in Traceable Layer Return Types, we often have classes that return other classes from various function calls. Some people may prefer to have a formal factory to use in that case instead. In this section we will discuss how to implement explicit factories for returned types.

First, we need to create the factories for the returned type: namespace FunctionalLayer.Factories { public class ReturnedTypeFactory { public virtual ReturnType Construct() { return new ReturnedType(); } } } namespace TraceableLayer.Factories { public class ReturnedTypeFactory : FunctionalLayer.Factories.ReturnedTypeFactory { private Tracer _tracer; public ReturnedTypeFactory(Tracer tracer) { _tracer = tracer; } public override ReturnType Construct() { return new TraceableLayer.Objects.ReturnedType(_tracer); } } }

Whenever the class that has a function that returns ReturnedType is created, it needs to have access to the returned type factory. The best way to do this is to pass the factory in as a constructor argument. namespace FunctionalLayer.Objects { public class ComplexType { private ReturnedTypeFactory _factory; public ComplexType(ReturnedTypeFactory factory) { _factory = factory; } public virtual ReturnedType FunctionCall() { var returned = _factory.Construct(); ... return returned; } } } namespace TraceableLayer.Objects { public class ComplexType : FunctionalLayer.Objects.ComplexType { private Tracer _tracer; public ComplexType(FunctionalLayer.Factories.ReturnedTypeFactory factory, Tracer tracer) : base(factory) { _tracer = tracer; } public override ReturnedType FunctionCall() { _tracer.OpenNode("ComplexType.FunctionCall"); var results = base.FunctionCall(); _tracer.AddAttribute("results", results); _tracer.CloseNode(); return results; } } }

Where this pattern gets more complicated is when we design the factories for the encapsulating class. It's factories need to also have a factory for the returned type. This way, when the outer class is created, it is automatically given the factory for the return type. namespace FunctionalLayer.Factories { public class ComplexTypeFactory { // needs to be protected so the Traceable factory may use it. protected ReturnedTypeFactory _returnedTypeFactory; public ComplexTypeFactory() : this(new ReturnedTypeFactory()) {} public ComplexTypeFactory(ReturnedTypeFactory factory) { _returnedTypeFactory = factory; } public virtual ComplexType Construct() { return new ComplexType(_returnedTypeFactory); } } } namespace TraceableLayer.Factories { public class ComplexTypeFactory : FunctionalLayer.Factories.ComplexTypeFactory { private Tracer _tracer; public ComplexTypeFactory(Tracer tracer) : this(new TraceableLayer.Factories.ReturnedTypeFactory(), tracer) {} public ComplexTypeFactory(FunctionalLayer.Factories.ReturnedTypeFactory factory, Tracer tracer) : base(factory) { _tracer = tracer; } public override ComplexType Construct() { // here is where we need access to _returnedTypeFactory return new TraceableLayer.Objects.ComplexType(_returnedTypeFactory, _tracer); } } }

Note that the ReturnedTypeFactory is a protected field in the Functional factory; this is necessary since the Traceable factory needs to have access to it. Also, we have two constructors, a default one, and one that takes the ReturnedTypeFactory as an argument.

The default constructor is fine if it is acceptable to simply construct the ReturnedTypeFactory and using it as is.

The constructor that takes the ReturnedTypeFactory has two important reasons to be used:

Design note: This implementation is very close to the one presented in Traceable Layer Return Types. If the ReturnedTypeFactory has no initialization logic in it, i.e. the factory only calls ReturnedType's constructors, then the style outlined in Traceable Layer Return Types is completely appropriate. The case where you want to make sure that you use Factories For Returned Types pattern is when the ReturnedTypeFactory has initialization code as discussed in Factories, Initializing. By using one factory, and passing it around, you are ensuring that you use the exact same initialization code everywhere.

Next: Daos