2

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.

2
  • you might also submit your actual code to codereview.stackexchange.com if you are looking for more specific feedback.
    – mcknz
    Mar 20, 2020 at 19:14
  • I'm voting to close this question as off-topic because entire blog posts and books have been written to answer this question. The question should focus on a particular problem.
    – RubberDuck
    Mar 21, 2020 at 17:09

1 Answer 1

5

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.

2
  • 2
    I'd go further than "don't be afraid to" and say it is absolutely essential to have a good suite of high-level functional tests, ideally automated ones, before you start any refactoring.
    – IMSoP
    Mar 21, 2020 at 11:27
  • Thank you for your answer! There are already minimal tests in place, but I may need to create some more. Unfortunately without making these changes they will involve changing the database, but I will have to lean on them until I refactor and I am able to inject the mock DB adapters.
    – dartimien
    Mar 23, 2020 at 16:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.