Is this a good or bad way to use constructor chaining? (... to allow for testing)
Asked Answered
H

2

6

My motivation for chaining my class constructors here is so that I have a default constructor for mainstream use by my application and a second that allows me to inject a mock and a stub.

It just seems a bit ugly 'new'-ing things in the ":this(...)" call and counter-intuitive calling a parametrized constructor from a default constructor , I wondered what other people would do here?

(FYI -> SystemWrapper)

using SystemWrapper;

public class MyDirectoryWorker{

    //  SystemWrapper interface allows for stub of sealed .Net class.
    private IDirectoryInfoWrap dirInf;

    private FileSystemWatcher watcher;

    public MyDirectoryWorker()
        : this(
        new DirectoryInfoWrap(new DirectoryInfo(MyDirPath)),
        new FileSystemWatcher()) { }


    public MyDirectoryWorker(IDirectoryInfoWrap dirInf, FileSystemWatcher watcher)
    {
        this.dirInf = dirInf;
        if(!dirInf.Exists){
            dirInf.Create();
        }

        this.watcher = watcher;

        watcher.Path = dirInf.FullName;

        watcher.NotifyFilter = NotifyFilters.FileName;
        watcher.Created += new FileSystemEventHandler(watcher_Created);
        watcher.Deleted += new FileSystemEventHandler(watcher_Deleted);
        watcher.Renamed += new RenamedEventHandler(watcher_Renamed);
        watcher.EnableRaisingEvents = true;
    }

    public static string MyDirPath{get{return Settings.Default.MyDefaultDirPath;}}

    // etc...
}
Hallock answered 23/3, 2010 at 23:50 Comment(2)
There's been a fair amount of discussion about this on SO. For example, see #300786. The general consensus seems to be that adding the default constructor can be unnecessarily confusing, though some argue that it's worth it when your library is used by callers that don't use a dependency injection framework.Carotid
Thanks for the link. In my case I'm writing a standalone application rather than a library. Dependency injection frameworks are another rung up the the curvaceous learning ladder from where I am now but something I should investigate.Hallock
G
3

Including a default constructor is a code smell as now the class is coupled to the concrete implementation of the IDirectoryInfoWrap. To make your life easier, use an IOC container external to the class to inject different dependencies depending on whether you are running test code or the mainstream application.

Gerhan answered 24/3, 2010 at 0:21 Comment(1)
martinfowler.com/bliki/InversionOfControl.html added to my reading list, thanks.Hallock
P
0

That's pretty much how I do it.

class MyUnitTestableClass
{
    public MyUnitTestableClass(IMockable foo)
    {
        // do stuff
    }

    public MyUnitTestableClass()
        : this(new DefaultImplementation())
    {
    }
}
Pedro answered 23/3, 2010 at 23:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.