Redundant code is code that changes at the same time for the same reason. Part of object oriented programming is limiting change to a single place. If it changes, it should change in one place.
How do µObjects encourage being nonredundant? By focusing on doing one thing.
Let's look at some specific attributes of µObjects that drive being nonredundant.
µObjects have redundancy with intent
Redundancy is intent based.
Redundancy isn't duplication.
If two things are doing the same thing for different reasons - That is not redundant. Even if the code is the same; it should be isolated and naming reflecting the intent of why it exists.
One of the big degradation of code is when you have two things using X; and one needs to do X, but a little different. That class starts to change to continue to support both uses. Booleans get passed in as flow control. Switch gets in to handle multiple cases.
That is wrong.
At this point it's clear that the modifications do not happen for the same reason. Whatever is changing should have its own version of an object doing this behavior; it changes for different reasons. The intent of the code is different; it's not redundant, it should be separated.
µObjects identify identical change
With µObjects it's easy to see if objects are changing for the same reason. You need to change one; you'll find near identical ones changing as well. There's not a lot going on. There's not a lot of difficulty in combining µObjects into an object that encapsulates that redundant behavior.
With µObjects; most of the time; if you need to change an object - You're writing a new one. It's hard to change something when it's doing one thing. If you change what it does... you probably need a new class for that new behavior.
µObjects don't repeat themselves
µObjects strongly discourage the DRY principle. While DRY is not itself redundant code; it can lead to redundant code.
µObjects do one thing. If you have two µObjects that do the same thing - You are by definition redundant with those two classes.
With a class that all it does is join strings
public class JoinText
private readonly string _seperator;
private readonly string _args;
public JoinText(string seperator, string args)
_seperator = seperator;
_args = args;
public string Value() => string.Join(_seperator, _args);
and we need to change it... What's there to change? There's nothing to change. With an object doing just one thing, not one job just one thing, you get objects that won't need to change much. Changes that do happen often show the need for more objects.
Changes lead to new objects which do that one new thing.
µObjects don't like to have the same thing done in multiple places; it's a huge smell and should get refactored into a new µObject.
µObject with redundancy is doing too much
If there are changes happening in a class the question must be asked, "Is this doing too much?"
Normally the answer is yes. The only subsequent change should be to refactor out behavior into a new µObject that is a new dependency. This oft leads to new refactors of encapsulating relationships. The relationship does one thing between the two objects. It exposes one behavior for the consumer.
Take whatever apparent redundant code and create a new object for it. With µObjects, repeated code is a smell; not redundancy, but if it exists in two places; it should be encapsulated in a µObject.
µObject redundancy is clear
µObjects stand out as odd when they have redundancy. They will be doing multiple things; not the one thing they should be doing.
Multiline methods are a smell. Not always reducable; but a smell. 7 lines? I expect 3 objects, or more, from that refactor.
There are not a lot of ways to do the one thing. There are a lot of ways to do something when you can do a lot of things in an object. With µObjects; you only do one thing. There's not a lot of ways to do the same one thing. Redundant code becomes very obvious.
Redundant code becomes very clear when you see an object being used the same way in multiple places instead of being asked to do something.
µObjects do one thing
As has been said a few times, µObjects do one thing. If it does two things; that's a violation of the µObject development practices.
What this leads to one of those things never becoming its own µObject and having to be re-written elsewhere, many times.
Taking a refactor from my Pizza Shop example.
I had a class to replace the last instance of a string. This was used to join a list and replace the last "," with "and".
public class ReplaceLastOfText : IText
private const int NotFound = -1;
private readonly IText _source;
private readonly IText _target;
private readonly IText _replace;
public ReplaceLastOfText(IText source, IText target, IText replace)
_source = source;
_target = target;
_replace = replace;
public string String()
string source = _source.String();
string target = _target.String();
int place = source.LastIndexOf(target, StringComparison.Ordinal);
return place == NotFound
: source.Remove(place, target.Length).Insert(place, _replace.String());
The string method is doing a few things. This is not an unreasonable class for procedural programming style; or most developers.
If I ever had to
Insert; I need to write that again. I need to do transformations, get values... There's a lot that goes into being able to remove and insert.
This class is not doing one thing. It does a lot.
This same behavior can be achieved by creating and combining a lot of classes doing a single thing; a lot of µObjects.
public class ReplaceLastOfText : IText
private readonly IText _origin;
public ReplaceLastOfText(IText source, IText target, IText replace) :
this(new InsertText(new RemoveText(source, target), new LastIndexOf(source, target), replace))
public ReplaceLastOfText(IText text) => _origin = text;
public string String() => _origin.String();
Here's the refactored class; achieving the same behavior. It does one thing. In this case; it knows how to combine the µObjects to do accomplish that one thing.
RemoveText uses another class
LengthOf. It also uses
RemoveText relies on information about the last index and length of inputs. As an object; I don't want to rely on someone telling me the correct data... Let me do that myself. I then have no concerns about the validity of the results. Were they correct to the target provided? Using the Dependency Constructor allows us to remove any of those questions from out µObject.
It has one job; Remove Text. It doesn't also have to do input validation.
I've covered it a bit in No Nulls; Input validation is removed for all but the edges of our application.
We don't want to introduce uncertainity and add redundancy by having to check inputs.
That'd be a hard redundancy to refactor away - We've designed it away.
µObjects do one thing - There won't be redundant code.
The simplest way for µObjects to maintain nonredundancy is to do one thing. If two things do the same thing; they will change for the same reason; make them one thing.