3

As part of a program I am developing for practice purposes, I've created a namespace Core designed to contain the core code resources for the program to use. This namespace is in a Class Library project.

Two of the sub-namespaces in Core are Core.Files and Core.Errors. In Core.Files there is a TxtFile class that is responsible for input-output to .txt files. In Core.Errors there is an Error class which is responsible for managing a specific error. The class API allows you to manage an error in three ways:

  1. Display the error to the user.
  2. Record the error in a log file.
  3. Perform both.

In the last two options, Error depends on TxtFile to record the error in the log file (using the Write() method).

The Write() method in TxtFile is built in such a way that if it fails to write text into the file, Error is instantiated. Since something is obviously not working in Write(), the error is displayed to the user but not logged in the file.

Everything works great. Over time, however, the two namespaces grew, and I thought it would be appropriate to create a separate project for each of them.

The problem is that since Error depends onTxtFile and TxtFile depends onError, there is a circular dependency between the two projects, and it is impossible to add references between them.

This answer suggests that such a situation is an indication of bad design. How can I improve the design so that this problem does not occur? I want to preserve the structure of a single assembly per sub-namespace.

Thanks.

1 Answer 1

3

If you are not interested in redesigning the object model, the following (fairly common) change will get you going again.

  1. Start a new class library called Interfaces or something similar.

  2. In both of your existing projects, add a reference to Interfaces.

  3. In the new library, define an interface IError and ITxtFile that match the methods and properties needed of both classes by both libraries.

  4. Modify the two existing libraries so that Error and TxtFile implement the interfaces you just created.

  5. Write factories for both TxtFile and Error. They don't have to be fancy.

  6. Modify the constructor for Error so that it takes a TxtFileFactory as a constructor argument. Use the factory to create the TxtFile (instead of the new keyword).

  7. Modify the constructor for TxtFile so that it takes a ErrorFactory as a constructor argument. Use the factory to create the Error (instead of the new keyword).

  8. Add TxtFileFactory and ErrorFactory to your dependency injection container.

  9. Modify your code so you only work with the interfaces, not the concrete types.

Explanation: Neither project will depend on the other for object creation. Instead, they will get their references from a factory that is injected. Because the object graph is defined in the composition root, the need for circular dependency for object creation is eliminated, and because of the interfaces defined in the third library, there is no longer any circular interface dependency.

2
  • Thank you so much for your answer. I am pretty new to design patterns, so I am not very familiar with dependency injection containers. Could you please modify your answer so a DIC is not required? Thanks again! Commented May 21, 2017 at 9:14
  • Could you help me, please? :) Commented May 27, 2017 at 20: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.