1

It's clear that the dependency inversion principle and the use of interfaces makes software components less coupled and promotes maintainability. On the other hand, these principles make using static methods and factories necessary. I think static methods should be out of any good object-oriented design, since they pollute the code with imperative programming practices that don't have anything to do with pure OOP.

My question is the following: is there a way to accomplish the DIP and effectively use interfaces to decouple components without using static factory methods? Is it so hard to design maintainable software with pure and simple OOP? Are encapsulation, inheritance and polymorphism really not enough?

  • 4
    These principles do not make using static methods and factories necessary. You can make your factories static if you so please, (and if nobody is looking,) but you don't have to, there is absolutely no such requirement, and even no such encouragement. – Mike Nakis May 30 '15 at 15:31
10

On the other hand, these principles make using static methods and factories necessary.

Since when?

I think static methods should be out of any good object-oriented design, since they pollute the code with imperative programming practices that don't have anything to do with pure OOP.

You could, but you'd be wrong. Modern OO design often includes static methods to incorporate functional programming concepts where appropriate. Remember that your job is to get stuff done, not purity. Sometimes the solution calls for a standalone (pure) function, so doing something else is making worse code.

Are encapsulation, inheritance and polymorphism really not enough?

These are not OO. They are the OO taught by Java schools, the foundation laid over a decade ago that people have reiterated since. They are really not enough, since they have very little impact on coupling, the key measure of code quality. Hell, inheritance itself is frowned upon these days in many situations.

Anyways, back to your code question:

is there a way to accomplish the DIP and effectively use interfaces to decouple components without using static factory methods?

Yes, it is super simple. You make a class that takes its dependencies (as interfaces) in its constructor. The code that uses that class either knows what concrete type to make, or has an instance of the interface itself (since it is a dependency). The code that uses that class passes the dependency in as a parameter. The class itself can then be wholy ignorant of the concrete type, getting the decoupling you want. No needless factories, no static methods.

  • 1
    I was taught OO is inheritance, encapsulation and polymorphism way before Java existed! I had a big poster of a clock explaining what the terms meant. – gbjbaanb May 30 '15 at 14:28
  • @gbjbaanb - cool, my experience largely does not go back that far. What was the target language if I might ask? C++? Smalltalk? – Telastyn May 30 '15 at 14:33
  • 1
    @Phil - you need a factory of some sort, since "I want to build some type based on some data, but I don't know the type" is the definition of a factory. Factories need not be static methods though, and I'm unsure how you think they do. – Telastyn May 30 '15 at 14:50
  • 1
    @phil - Depends on the language. Even in Java you can now do functional stuff. – Telastyn May 30 '15 at 15:23
  • 1
    @phil - I don't see how pure imperative C-like functions preclude functional capabilities. If anything, they lend themselves to functional programming far better than instance methods. – Telastyn May 30 '15 at 23:18
2

Static methods are bad – not because they are “pre-OOP”, but precisely because they themselves are dependencies that cannot be inverted. I was recently trying to test a codebase that involved a call such as

void foo() {
    ...
    Time now = Clock::getCurrentSystemTime();
    ...
}

It makes sense that this is static since (a) this code is performance-sensitive, and (b) there can only be one current time. Unfortunately this meant I could not inject a Clock of my own for these tests so that I could test the behaviour of the system around certain times. In other words, that codebase violated the Dependency Inversion Principle.

Dependency inversion is simple: I just have to make the dependency explicit, and pass it as a parameter to the current code unit (e.g. object constructor, or method parameter):

type CurrentTimeProvider = () => Time;

void foo(CurrentTimeProvider currentTime) {
    ...
    Time now = currentTime();
    ...
}

// in production:
foo(Clock::getCurrentSystemTime);

// in testing:
foo(() => Time("2000-01-01T00:00:00.000Z"));

There are no static methods involved here in any meaningful way. In more complicated cases, we would perhaps introduce a fully fledged Dependency Injection Container that takes care of resolving all dependencies. The DI container is itself an abstract factory object. Ideally, it would be passed around explicitly. Then:

type CurrentTimeProvider = () => Time;

void foo(DIContainer deps) {
    ...
    Time now = deps.get(CurrentTimeProvider)();
    ...
}

DiContainer deps;

// in production:
deps.set(CurrentTimeProvider, Clock::getCurrentSystemTime);
foo(deps);

// in testing:
deps.set(CurrentTimeProvider, () => Time("2000-01-01T00:00:00.000Z"));
foo(deps);

Well, but people quickly get tired of any kind of manual dependency injection. So assuming that an un-invertable dependency on some dependency injection system is OK, we might make the DI container API static (or make the container a singleton, which is essentially the same thing). The above example might change to:

type CurrentTimeProvider = () => Time;

void foo() {
    ...
    Time now = DIContainer.get(CurrentTimeProvider)();
    ...
}

// in production:
DIContainer.set(CurrentTimeProvider, Clock::getCurrentSystemTime);
foo();

// in testing:
DIContainer.set(CurrentTimeProvider, () => Time("2000-01-01T00:00:00.000Z"));
foo();

Actually, that's fairly elegant and sufficiently sane. Is this dazzlingly beautiful OOP design? Probably not, but you won't run into any kind of problem with this (unless you try to mock the dependency injection system itself). If you don't like such a global DI system (and I don't like it either), you can pick any of the other available strategies available for DI (again: explicit parameter/constructor injection, or injection of a DI container/factory object). In a language-agnostic view, the only thing in your program that can't be static is your program's entry point/main function.

1

If you really want to make your classes decoupled, then you will ignore all compile-time constructs that statically link the methods together and will go to the original OO definition of method calls, which is message passing.

Of course, this will make your code more difficult to manage (potentially, I've had great success with distributed systems that use a message passing architecture) or debug via compiler errors.

IIRC, IoC requires you to pass in the 'global' configuration object to each object via its constructor rather than the object fetching this config object when needed. Various container frameworks for IoC and DI may use statics, factories and whatnot, but you don;t have to use them. Whatever you do, do not get a framework's design choices confused for the fundamental aspect of the pattern you're using.

So, given the system you're working with, you have to trade off idealism for practicality. If this means using static methods, or globals or (horror!!!) even singletons, then use them. Your job is not to make perfect code, its to make easily understandable, easily maintainable code that fulfils its role in producing a product that does something useful for people.

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.