I'm working on a classes that are designed to read a defined file format; said format is comprised of 2 separate files (FileA
and FileB
). For ease of use, the format is referred to as the FileA
format, but a valid "file" must have a FileB
. The files share the same path prefix, but alter in suffix; therefore my current classes appears as:
class FileReaderA:
def __init__(self, path: pathlib.Path):
self.a = path.with_suffix('.fa')
self.b = FileReaderB(path)
# methods related to operating on FileA types
class FileReaderB:
def __init__(self, path: pathlib.Path):
self.b = path.with_suffix('.fa')
# methods related to operating on FileB types
The classes are separated since FileReaderB
does not depend on FileReaderA
and can be used independently to read FileB
types, however a valid FileA
requires its associated FileB
. Therefore, composition made sense, at first. Now I'm wondering if DI is the more appropriate solution, but it requires that FileReaderB
be explicitly instantiated and passed to the constructor of FileReaderA
, which is less than ideal form a user standpoint with this API:
>>> frb = FileReaderB(path)
>>> fra = FileReaderA(path, frb)
Thus I am forced to abstract it by using a factory:
def reader(path: pathlib.Path):
return FileReaderA(path, FileReaderB(path))
>>> fra = reader(path)
This seems verbose and I feel as though I'm introducing more code and layers of abstraction to achieve the same result, all around the guise for better unit testing of decoupled classes.
What is wrong with just using Composition over enforcing DI?
FileReaderA
depends uponFileReaderB
? On the surface it sounds like you have two separate file formats. To accomplish some goal, an operation may need bothFileReaderA
andFileReaderB
at the same time, but is this a responsibility ofFileReaderA
, or can it be pulled out into some other class?FileReaderA
needs the information of Record 1 ofFileReaderB
. Independently their data is valid, but means nothing when disassociated.Processer
and it must instantiate each of the reader abstractions in its constructor; thus we've arrived at composition.