'. '

EliminateFuzzyModifiers

From APIDesign

(Difference between revisions)
Jump to: navigation, search
Line 1: Line 1:
''Fuzzy'' access modifiers are source of all evil as described at [[ClarityOfAccessModifiers]] page. Good message is that we do not need them. Our [[API]]s can be [[Clarity|clear]] enough with use of following easy steps.
''Fuzzy'' access modifiers are source of all evil as described at [[ClarityOfAccessModifiers]] page. Good message is that we do not need them. Our [[API]]s can be [[Clarity|clear]] enough with use of following easy steps.
-
== Public Abstract ==
+
The examples on this page are slightly enhanced to those discussed [[Cooperating_with_Other_APIs|Chapter 10]], Cooperating with Other APIs, where this topic is deeply discussed too.
 +
 
 +
=== Public Abstract ===
Imagine that there is a class with a single '''public''' '''abstract''' method like in this example:
Imagine that there is a class with a single '''public''' '''abstract''' method like in this example:
Line 19: Line 21:
<source lang="java" snippet="sidemeanings.PublicAbstract.Clean.test"/>
<source lang="java" snippet="sidemeanings.PublicAbstract.Clean.test"/>
-
Eliminiating '''public''' '''abstract''' modifiers is easy, if found desirable.
+
Eliminating '''public''' '''abstract''' modifiers is easy, if found desirable.
 +
 
 +
=== Protected ===
 +
 
 +
'''protected''' methods are also [[ClarityOfAccessModifiers|said to have double meaning]]]. They can be overriden, plus they can carry some implementation for subclasses to call just like in this example:
 +
 
 +
<source lang="java" snippet="sidemeanings.Protected.Dirty"/>
 +
 
 +
Similarly like in previous case, this ''fuzziness'' can be eliminated by splitting the method into two:
 +
 
 +
<source lang="java" snippet="sidemeanings.Protected.Clean"/>
 +
 
 +
The expressive power of such [[API]] remains unchanged. Once can still rewrite code that is using the old version:
 +
 
 +
<source lang="java" snippet="sidemeanings.Protected.Dirty.test"/>
 +
 
 +
into code written against new version quite easily:
 +
 
 +
<source lang="java" snippet="sidemeanings.Protected.Clean.test"/>
 +
 
 +
Just instead of calling ''super.increment()'' you need to delegate to the '''protected''' '''final''' ''defaultImplementation''. The rest of the code stays the same. It is so easy to [[Clarity|clarify purpose]] of '''protected''' methods!
 +
 
 +
=== Public ===
 +
 
 +
The plain '''public''' modifier has in fact even more meanings. Just writing:
 +
 
 +
<source lang="java" snippet="sidemeanings.Public.Dirty"/>
 +
 
 +
may actually mean three different things. In the process of [[Clarity|cleaning the meaning]] we thus need to split the method in following way:
 +
 
 +
<source lang="java" snippet="sidemeanings.Public.Clean"/>
 +
 
 +
Again this does not make any use more complex. The old test:
 +
 
 +
<source lang="java" snippet="sidemeanings.Public.Dirty.test"/>
 +
 
 +
needs just two modifications - name of overriden method is different and instead of calling '''super''' implementation, we call the default one:
 +
 
 +
<source lang="java" snippet="sidemeanings.Public.Clean.test"/>
 +
 
 +
Getting rid of multi-meaning '''public''' modifiers is easy too.
 +
 
 +
== Complains? ==
-
TBD: Meanwhile see the book's [[Cooperating_with_Other_APIs|Chapter 10]], Cooperating with Other APIs where this topic is discussed.
+
TBD: Complex? Not that much, moreover correct.
 +
TBD: Hard for reader of the API. True:
 +
TBD: Split into different classes [[Cooperating_with_Other_APIs|Chapter 10]], Cooperating with Other APIs

Revision as of 21:43, 25 March 2009

Fuzzy access modifiers are source of all evil as described at ClarityOfAccessModifiers page. Good message is that we do not need them. Our APIs can be clear enough with use of following easy steps.

The examples on this page are slightly enhanced to those discussed Chapter 10, Cooperating with Other APIs, where this topic is deeply discussed too.

Contents

Public Abstract

Imagine that there is a class with a single public abstract method like in this example:

Code from PublicAbstract.java:
See the whole file.

public abstract void increment();
 

This indeed violates the clarity rules and in many cases API designers may wish to eliminate it. The change is simple. Just split the method into two:

Code from PublicAbstract.java:
See the whole file.

public final void increment() {
    overridableIncrement();
}
protected abstract void overridableIncrement();
 

Now both methods use single meaning access modifiers, so the so wanted clarity is achieved. Moreover the usage of such API is made complex. Compare the original snippet with classical modifier:

Code from PublicAbstractTest.java:
See the whole file.

class DoubleIncrement extends PublicAbstract.Dirty {
    int counter;
 
    @Override
    public void increment() {
        counter += 2;
    }
}
DoubleIncrement doubleIncr = new DoubleIncrement();
doubleIncr.incrementTenTimes();
Assert.assertEquals(20, doubleIncr.counter);
 

With the same code rewritten to clean version with two methods. Everything remains the same, just instead of overriding the final method, one needs to override the protected abstract one:

Code from PublicAbstractTest.java:
See the whole file.

class DoubleIncrement extends PublicAbstract.Clean {
    int counter;
 
    @Override
    protected void overridableIncrement() {
        counter += 2;
    }
}
DoubleIncrement doubleIncr = new DoubleIncrement();
doubleIncr.incrementTenTimes();
Assert.assertEquals(20, doubleIncr.counter);
 

Eliminating public abstract modifiers is easy, if found desirable.

Protected

protected methods are also said to have double meaning]. They can be overriden, plus they can carry some implementation for subclasses to call just like in this example:

Code from Protected.java:
See the whole file.

protected void increment() {
    // implementation:
    counter++;
}
 

Similarly like in previous case, this fuzziness can be eliminated by splitting the method into two:

Code from Protected.java:
See the whole file.

protected abstract void increment();
protected final void defaultIncrement() {
    counter++;
}
 

The expressive power of such API remains unchanged. Once can still rewrite code that is using the old version:

Code from ProtectedTest.java:
See the whole file.

class DoubleIncrement extends Protected.Dirty {
    @Override
    protected void increment() {
        super.increment();
        super.increment();
    }
}
DoubleIncrement doubleIncr = new DoubleIncrement();
doubleIncr.incrementTenTimes();
doubleIncr.assertCounter(20);
 

into code written against new version quite easily:

Code from ProtectedTest.java:
See the whole file.

class DoubleIncrement extends Protected.Clean {
    @Override
    protected void increment() {
        // cannot be access directly, it is abstract:
        // super.increment();
        // we need to call default implementation instead
        defaultIncrement();
        defaultIncrement();
    }
}
DoubleIncrement doubleIncr = new DoubleIncrement();
doubleIncr.incrementTenTimes();
doubleIncr.assertCounter(20);
 

Just instead of calling super.increment() you need to delegate to the protected final defaultImplementation. The rest of the code stays the same. It is so easy to clarify purpose of protected methods!

Public

The plain public modifier has in fact even more meanings. Just writing:

Code from Public.java:
See the whole file.

public void increment() {
    // internal implementation
    counter++;
}
 

may actually mean three different things. In the process of cleaning the meaning we thus need to split the method in following way:

Code from Public.java:
See the whole file.

public final void increment() {
    overridableIncrement();
}
protected abstract void overridableIncrement();
protected final void defaultIncrement() {
    counter++;
}
 

Again this does not make any use more complex. The old test:

Code from PublicTest.java:
See the whole file.

class DoubleIncrement extends Public.Dirty {
    @Override
    public void increment() {
        super.increment();
        super.increment();
    }
}
DoubleIncrement doubleIncr = new DoubleIncrement();
doubleIncr.incrementTenTimes();
doubleIncr.assertCounter(20);
 

needs just two modifications - name of overriden method is different and instead of calling super implementation, we call the default one:

Code from PublicTest.java:
See the whole file.

class DoubleIncrement extends Public.Clean {
    @Override
    protected void overridableIncrement() {
        defaultIncrement();
        defaultIncrement();
    }
}
DoubleIncrement doubleIncr = new DoubleIncrement();
doubleIncr.incrementTenTimes();
doubleIncr.assertCounter(20);
 

Getting rid of multi-meaning public modifiers is easy too.

Complains?

TBD: Complex? Not that much, moreover correct. TBD: Hard for reader of the API. True: TBD: Split into different classes Chapter 10, Cooperating with Other APIs

buy