I'd like to dig into that situation a little with an example. Imagine a design where one class provides a method used by many others.
A basic static structure diagram |
In all cases, the name of the method and certain expectations about its signature are replicated throughout the system. That’s not really a problem, though. In fact, it makes a problem go away: wherever that method call exists, there could just be a pile of duplicated code, instead.
If we’re talking about a compiled language and static types, this is a no-brainer. If you need to change the signature, there are probably tools to do it for you. Even if the tools don’t work or you choose not to use them, the compiler will catch most of the mistakes you’re likely to make.
If you use a compiler, method calls don't count as redundancy |
If we’re talking about a non-compiled language or a dynamically-typed system of classes, the duplication becomes an issue. The purpose of a method signature is to serve as a contract between two objects. If that contract changes, it needs to be changed at all touch-points.
You can get around that problem by changing how you change things or changing how you find things. For instance, you can rely more-heavily on integration tests to make sure every combination of objects that might need to work together can do so successfully.
Integration tests can do part of the job of a compiler |
You might also rely more heavily on design, encapsulating intentions into their own classes and methods and decoupling the expression of intent from its fulfillment. This would mean that changing a signature (from the caller's perspective) would not need to be a "find and fix" problem. It could just be a "change one thing" problem.
Encapsulating a relationship turns redundancy from not using a compiler into non-redundancy |
If you are careful to rely upon that indirection throughout your tests, too, you can enforce that the design change is propagated to all implementations of an interface.
The point, here, is to drive home the fact that redundancy doesn't exist in the duplication of form. It's not even like you can say all replicated purposes are redundancy. In a compiled, statically-typed language, method calls all represent a replicated purpose that is nonredundant because the compiler finds them all for you. In a language without those protections, the exact same design suddenly represents redundancy.
The situation is part of deciding whether or not something is redundant.