Reinier Torenbeek
Reinier Torenbeek

Reputation: 17383

What is a good approach to encapsulate the creation and finalization of an object?

I am using a third party assembly with a bunch of classes, organized in a hierarchical way. They have private constructors and get instantiated by invoking a CreateXxx() method on an instance of another class one level higher. On the top of the hierarchy, a singleton class is used. For example, application code looks something like this:

TopClass top = TopClass.GetInstance();
SecondLevelClass secondLevel = top.CreateSecondLevelElement();
ThirdLevelClass thirdLevel = secondLevel.CreateThirdLevelElement();

Similarly, finalization is done using a DeleteXxx() method, which has to happen on the same object used for CreateXxx().

I will not go into the reasons why this particular approach was taken. However, I would like to encapsulate the creation for these classes with my own set of classes that do have public constructors, such that the code will look better, like this:

// No TopClass anymore, see explanation below
MySecondLevelClass secondLevel = new MySecondLevelClass();
MyThirdLevelClass thirdLevel = new MyThirdLevelClass(secondLevel);

My initial approach was for the MyXxx classes to invoke the relevant CreateXxx() method in the constructor and maintain a reference to the result. Also, a reference to the constructor parameter is required in order to invoke the DeleteXxx() method on at finalization time. For that, I was thinking about implementing IDisposable and invoke DeleteXxx() inside Dispose().

With this approach, the TopClass has become superfluous for the application developer, because its single purpose was the creation and destruction of SecondLevelClass instances. This now happens under the hood inside the MySecondLevelClass constructor.

All in all, I think this is technically sound. The only problem I run into is the fact that the original classes implement interfaces that have a lot of methods, which I still want to be exposed via my MyXxx classes. I am not looking forward to re-defining all those methods on my own classes, only to forward all invocations to the encapsulated reference to the instance of the "original" class . It would be nice if I could do something like overloading the dot operator. Or, it would be nice to tell the compiler that the encapsulated reference to the instance of the original class actually implements the interface -- sort of adding an extra indirection in the VMT.

Those options are not supported of course, and I have not found a way around this duplication of all methods. Does anybody have an idea of how to do this in a smarter way? Any feedback is appreciated, thanks in advance.

Upvotes: 2

Views: 193

Answers (2)

theMothaShip
theMothaShip

Reputation: 1211

The only way you could accomplish this (as far as I know) would be to do something like the following --


public class Concrete : SomeInterface
{
    private Concrete()
    {
        //instantiate other classes here
    }
    public string GetAString()
    {
        return string.Empty;
    }

    public static Concrete GetInstance()
    {
        return new Concrete();
    }

}

public class Wrapper : SomeInterface
{
    public Wrapper()
    {
        this.concrete = Concrete.GetInstance();
    }
    Concrete concrete { get; set; }
    public string GetAString()
    {
        this.concrete.GetAString();
    }
}

public interface SomeInterface
{
    string GetAString();
}

I know it isn't the best approach as you would have to manually call out to each interface member on the class you are wrapping but it would accomplish the goal.

Maybe try out T4 code templates? If you come across this situation often (and the interfaces change) it may save you time in the future?

Upvotes: 0

RJ Lohan
RJ Lohan

Reputation: 6527

Perhaps, rather than wrapping the existing classes, you can wrap their construction using something like a builder pattern. You won't get 'TopClass o = new TopClass()' syntax, but a construction pattern like this is well understood, and should improve understanding of your code.

A (basic) builder could look like;

public class OtherAssemblyBuilder
{
    public TopClass Build()
    {
        return TopClass.GetInstance();
    }

    public SecondLevelClass Build()
    {
        TopClass top = TopClass.GetInstance();
        return top.CreateSecondLevelElement();
    }

    public ThirdLevelClass Build()
    {
        TopClass top = TopClass.GetInstance();
        SecondLevelClass secondLevel = top.CreateSecondLevelElement();
        return secondLevel.CreateThirdLevelElement();
    }
}

Upvotes: 1

Related Questions