Question

I am working on wrapping an old project written in C# in a test framework.

The largest problem I have is that I have a bunch of classes that are all VERY tightly coupled with other classes. All of these classes also make their own calls to the database, and many classes instantiate others directly within their methods.

I think I have come up with a good resolution to the problem, but I want to do a quick sanity check. I was thinking of moving instantiation of all objects to an abstract factory. Instead of directly instantiating adapters that speak to the database within the class, I will use Dependency Inversion to pass the adapter to the object at instantiation within the factory.

I will then be able to create a test factory that passes in mocked adapters to the objects instead of the ones that directly call the database.

When objects instantiate other objects within their own methods, they need to use the same type of factory that instantiated themselves. I was thinking of passing the factory that created the object into its constructor to save as a property when they want to make other objects. Would that be a good idea?

Sorry for the ramble, let me know if people need more information.

Was it helpful?

Solution

You've got the basic pattern. Define interfaces for the classes that communicate with out-of-process resources, like databases and file systems. Then have the "real" class implement the interface. Expose a constructor that takes an object by that interface as an argument, and redefine the type for that field or property to be the interface rather than the concrete class.

Constructors that instantiate objects should just pass them to the other constructor that takes the objects as parameters.

I'm not so sure an abstract factory is necessary here. If it works, use it, but it does add another layer of complexity to an already complex and tightly coupled application. I would start out with Poor Man's DI: constructor arguments.

Bigger picture, this refactoring job should be done as a series of surgical procedures. Take small little slices of the application and apply this design methodology. The scalpel is your best friend. While you might be tempted to just refactor as much as you can in one fell swoop, you'll affect too much of the application. Little bits. This is an elephant. Eat this meal in little bites. Target the high value areas of the application first, where the most defects occur or the feature is the most important to your end users or bottom line.

Before you start refactoring, don't be afraid to put together a few end-to-end tests hitting the major use cases of the application. That way you can ensure those cases still function as expected from a macro perspective as you make all of these surgical cuts.

Licensed under: CC-BY-SA with attribution
scroll top