0

Question

I apologize if this question has already been posted. I tried searching but I couldn't find the answer.

I often find myself trying to use DI and DIP where I need to open a file or a socket. Using DI comes naturally when doing TDD as it is sometimes hard and/or ugly to test that something has been written correctly to a file or a socket.

  • How can one use DI and DIP for built-in or external libraries?
  • Is this even a proper way of doing things or am I overdoing it?

Thank you in advance.

Code

I have a simple Key-Value store which creates one file per Key and writes the Value to that file.

from abc import ABCMeta, abstractmethod

class DiskInterface(metaclass=ABCMeta):

  @abstractmethod
  def open(self, path, mode):
    pass

  @abstractmethod
  def close(self):
    pass

  @abstractmethod
  def read(self):
    pass

  @abstractmethod
  def write(self, value):
    pass

  @abstractmethod
  def exists(path):
    pass


class DiskStore:

  def __init__(self, disk_if):
    assert isinstance(disk_if, DiskInterface)
    self.disk_if = disk_if

  def set(self, key, value):
    assert isinstance(key, str)
    assert isinstance(value, str)

    f = self.disk_if.open("/tmp/" + key, "w")
    f.write(value)
    f.close()

  def get(self, key):
    assert isinstance(key, str)

    key_file = "/tmp" + key 
    if self.disk_if.exists(key_file):
      f = self.disk_if.open(key_file, "r")
      ret = f.read()
      f.close()
      return ret 
    else:
      return None

# Unit tests

def test_set_and_get():
  if_stub = DiskIFStub()
  kvstore = DiskStore(disk_if=if_stub)

  kvstore.set("answer", "42")

  assert kvstore.get("answer") == "42"

class DiskIFStub(DiskInterface):

  def __init__(self):
    self.opened = False

  def open(self, path, mode):
    self.opened = True
    return self

  def close(self):
    self.opened = False

  def read(self):
    assert self.opened
    return self.v

  def write(self, value):
    assert self.opened
    self.v = value

  @staticmethod
  def exists(path):
    return True
1

As a general, largely language agnostic approach, any time you're concerned that the behavior of a third party library might change, the providing of that behavior (be it your own code in the future, or another third party library) might change, or you simply want to decouple the third party library, it's perfectly reasonable to build a more abstracted wrapper class around the library object(s).

If the language allows, the wrapper can be an interface, to further decouple and abide by the mindset of the Dependency Inversion Principle.

| improve this answer | |
  • Thank you for your answer. So I take as my thinking by this approach is correct then, although I use built-in functionality in this case. – Tomas Vestelind Jun 1 '16 at 15:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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