11

I've been working on refactoring some code, and I think I may have taken the first step down the rabbit hole. I'm writing the example in Java, but I suppose it could be agnostic.

I have an interface Foo defined as

public interface Foo {

    int getX();

    int getY();

    int getZ();
}

And an implementation as

public final class DefaultFoo implements Foo {

    public DefaultFoo(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getZ() {
        return z;
    }

    private final int x;
    private final int y;
    private final int z;
}

I also have an interface MutableFoo that provides matching mutators

/**
 * This class extends Foo, because a 'write-only' instance should not
 * be possible and a bit counter-intuitive.
 */
public interface MutableFoo extends Foo {

    void setX(int newX);

    void setY(int newY);

    void setZ(int newZ);
}

There are a couple of implementations of MutableFoo that could exist (I haven't implemented them yet). One of them is

public final class DefaultMutableFoo implements MutableFoo {

    /**
     * A DefaultMutableFoo is not conceptually constructed 
     * without all values being set.
     */
    public DefaultMutableFoo(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public int getX() {
        return x;
    }

    public void setX(int newX) {
        this.x = newX;
    }

    public int getY() {
        return y;
    }

    public void setY(int newY) {
        this.y = newY;
    }

    public int getZ() {
        return z;
    }

    public void setZ(int newZ) {
        this.z = newZ;
    }

    private int x;
    private int y;
    private int z;
}

The reason I have split these is because it is equally likely for each to be used. Meaning, it is equally likely that someone using these classes will want an immutable instance, as it is they will want a mutable one.

The primary use-case that I have is an interface called StatSet that represents certain combat details for a game (hitpoints, attack, defense). However, the "effective" stats, or the actual stats, are a result of the base stats, that can never be changed, and the trained stats, which can be increased. These two are related by

/**
 * The EffectiveStats can never be modified independently of either the baseStats
 * or trained stats. As such, this StatSet must never provide mutators.
 */
public StatSet calculateEffectiveStats() {
    int effectiveHitpoints =
        baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
    int effectiveAttack = 
        baseStats.getAttack() + (trainedStats.getAttack() / 4);
    int effectiveDefense = 
        baseStats.getDefense() + (trainedStats.getDefense() / 4);

    return StatSetFactory.createImmutableStatSet(effectiveHitpoints, effectiveAttack, effectiveDefense);
}

the trainedStats are increased after every battle like

public void grantExperience() {
    int hitpointsReward = 0;
    int attackReward = 0;
    int defenseReward = 0;

    final StatSet enemyStats = enemy.getEffectiveStats();
    final StatSet currentStats = player.getEffectiveStats();
    if (enemyStats.getHitpoints() >= currentStats.getHitpoints()) {
        hitpointsReward++;
    }
    if (enemyStats.getAttack() >= currentStats.getAttack()) {
        attackReward++;
    }
    if (enemyStats.getDefense() >= currentStats.getDefense()) {
        defenseReward++;
    }

    final MutableStatSet trainedStats = player.getTrainedStats();
    trainedStats.increaseHitpoints(hitpointsReward);
    trainedStats.increaseAttack(attackReward);
    trainedStats.increaseDefense(defenseReward);
}

but they aren't increased just after battle. Using certain items, employing certain tactics, clever use of the battlefield all can grant different experience.

Now for my questions:

  1. Is there a name for splitting interfaces by accessors and mutators into separate interfaces?
  2. Is splitting them in this way the 'right' approach if they are equally likely to be used, or is there a different, more accepted pattern that I should use instead (eg. Foo foo = FooFactory.createImmutableFoo(); which could return DefaultFoo or DefaultMutableFoo but is hidden because createImmutableFoo returns Foo)?
  3. Are there any immediately foreseeable downsides to using this pattern, save for complicating the interface hierarchy?

The reason I started designing it this way is because I'm of the mindset that all implementers of an interface should adhere to the simplest interface possible, and provide nothing more. By adding setters to the interface, now the effective stats can be modified independently of its parts.

Making a new class for the EffectiveStatSet does not make much sense as we're not extending the functionality in any way. We could change the implementation, and make an EffectiveStatSet a composite of two different StatSets, but I feel that is not the right solution;

public class EffectiveStatSet implements StatSet {

    public EffectiveStatSet(StatSet baseStats, StatSet trainedStats) {
        // ...
    }

    public int getHitpoints() {
        return baseStats.getHitpoints() + (trainedStats.getHitpoints() / 4);
    }
}
  • 4
  • 1
    I think the problem is your object is mutable even if you are accessing it my its 'immutable' interface. Better to have a mutable object with Player.TrainStats() – Ewan Jun 23 '15 at 18:54
  • @gnat do you have any references to where I can learn more about that? Based on the edited question, I'm not sure how or where I could apply that. – Zymus Jun 24 '15 at 3:43
  • @gnat: your links (and its second-degree and third-degree links) are very useful, but catchy phrases of wisdom are of little help. It only attracts misunderstanding and despise. – rwong Aug 2 '15 at 6:52
6
+50

To me it seems you have a solution looking for a problem.

Is there a name for splitting interfaces by accessors and mutators into separate interfaces?

This might be a little provoking, but actually I would call it "overdesigning things" or "overcomplicating things". By offering a mutable and an immutable variant of the same class, you offer two functionally equivalent solutions for the same problem, which differ only in non-functional aspects like performance behaviour, API and security against side effects. I guess that is because you fear to make a decision which one to prefer, or because you try to implement the "const" feature of C++ in C#. I guess in 99% of all cases it will not make a big difference if the user picks the mutable or the immutable variant, he can solve his problems with the either or the other. Thus "likeliness of a class to be used" is probably result of what you offer in your library - if you offer two almost similar solutions to the same problem, expect a 50% chance that the users will pick variant A or B. If you offer just one solution, the likeliness is high they will use that solution, and will be happy with it.

The exception is when you design a new programming language or a multi purpose framework which is going to be used by some ten thousands of programmers or more. Then it can indeed scale better when you offer immutable and mutable variants of general purpose data types. But this is a situation where you will have thousands of different usage scenarios - which is probably not the problem you face, I guess?

Is splitting them in this way the 'right' approach if they are equally likely to be used or is there a different, more accepted pattern

The "more accepted pattern" is called KISS - keep it simple and stupid. Make a decision for or against mutability for the specific class/interface in your library. For example, if your "StatSet" has a dozen of attributes or more, and they are mostly changed individually, I would prefer the mutable variant and just not modify the base stats where they should not be modified. For something like a Fooclass with attributes X,Y,Z (a three dimensional vector), I would probably prefer the immutable variant.

Are there any immediately foreseeable downsides to using this pattern, save for complicating the interface hierarchy?

Overcomplicated designs make software harder to test, harder to maintain, harder to evolve.

  • 1
    I think you mean "KISS - Keep It Simple, Stupid" – Epicblood Aug 8 '15 at 0:14
2

Is there a name for splitting interfaces by accessors and mutators into separate interfaces?

There could be a name for this if this seperation is usefull and provides a benefit which i donot see. If the separatons does not provide a benefit the other two questions make no sense.

Can you tell us any business use-case where the two seperate Interfaces provide us benefit or is this question an academic problem (YAGNI)?

I can think of shop with a mutable cart (you can put more articles in) that can become an order where the articles cannote be changed by the customer any more. The order-status is still mutable.

Implementations that i have seen do not seperate the interfaces

  • there is no interface ReadOnlyList in java

The ReadOnly version uses the "WritableInterface" and throws an exception if a write method is uesed

  • I've modified the OP to explain the primary use-case. – Zymus Jun 23 '15 at 16:32
  • 2
    "there is no interface ReadOnlyList in java" - frankly, there should be. If you have a piece of code that accepts a List<T> as a parameter, you can't easily tell if it will work with an unmodifiable list. Code that returns lists, there's no way of knowing whether you can safely modify them. The only option is either relying on potentially incomplete or inaccurate documentation, or making a defensive copy just in case. A read only collection type would make this much simpler. – Jules Aug 2 '15 at 12:43
  • @Jules: indeed, the Java way seems to be all of above: to make sure documentation is complete and accurate, as well as making a defensive copy just in case. It surely scales to very large enterprisey projects. – rwong Aug 8 '15 at 5:09
1

The separation of a mutable collection interface and a read-only-contract collection interface is an example of interface segregation principle. I don't think there is any special names given to each application of principle.

Notice a few words here: "read-only-contract" and "collection".

A read-only-contract means that class A gives class B a read-only access, but does not imply that the underlying collection object is actually immutable. Immutable means that it shall never change in the future, no matter by any agent. A read-only-contract only says that the recipient is not allowed to change it; someone else (in particular, class A) is allowed to change it.

To make an object immutable, it must be truly immutable - it must deny attempts to change its data regardless of the agent requesting it.

The pattern is most likely observed on objects that represents a collection of data - lists, sequences, files (streams), etc.


The word "immutable" becomes fad, but the concept is not new. And there are many ways of using immutability, as well as many ways of achieving better design using something else (i.e. its competitors).


Here is my approach (not based on immutability).

  1. Define a DTO (data transfer object), also known as a value tuple.
    • This DTO will contain the three fields: hitpoints, attack, defense.
    • Just fields: public, anyone can write, no protection.
    • However, a DTO must be a throwaway object: if class A needs to pass a DTO to class B, it makes a copy of it and pass the copy instead. So, B can use the DTO however it likes (writing to it), without affecting the DTO that A holds on to.
  2. The grantExperience function to be broken down into two:
    • calculateNewStats
    • increaseStats
  3. calculateNewStats will take the inputs from two DTO, one representing the player stats and another representing the enemy stats, and perform the calculations.
    • For the input, the caller should choose among the base, trained or effective stats, according to your needs.
    • The result will be a new DTO where each field (hitpoints, attack, defense) stores the amount to be incremented for that ability.
    • The new "amount to increment" DTO does not concern about the upper limit (imposed maximum cap) for those values.
  4. increaseStats is a method on the player (not on the DTO) that takes an "amount to increment" DTO, and applies that increment on the DTO that is owned by the player and represents the player's trainable DTO.
    • If there are applicable maximum values for these stats, they are enforced here.

In case calculateNewStats is found to not depend on any other player or enemy information (beyond the values in the two input DTO), this method can potentially be located anywhere in the project.

If the calculateNewStats is found to have a full dependency on the player and enemy objects (that is, future player and enemy objects may have new properties, and calculateNewStats must be updated to consume as much of their new properties as possible), then calculateNewStats must accept those two objects, not merely the DTO. However, its calculation result will still be the increment DTO, or whatever simple data-transfer objects that carries the information that is used to perform an increment/upgrade.

1

There's a big problem here: just because a data structure is immutable doesn't mean we don't need modified versions of it. The real immutable version of a data structure does provide setX, setY, and setZ methods - they just return a new structure instead of modifying the object you called them on.

// Mutates mutableFoo
mutableFoo.setX(...)
// Creates a new updated immutableFoo, existing immutableFoo is unchanged
newFoo = immutableFoo.setX(...)

So how do you give one part of your system the ability to change it while restricting other parts? With a mutable object containing a reference to an instance of the immutable structure. Basically, instead of your player class being able to mutate its stats object while giving every other class an immutable view of its stats, its stats are immutable and the player is mutable. Instead of:

// Stats are mutable, mutates self.stats
self.stats.setX(...)

You'd have:

// Stats are immutable, mutates self, setX() returns new updated stats
self.stats = self.stats.setX(...)

See the difference? The stats object is completely immutable, but the player's current stats is a mutable reference to the immutable object. No need to make two interfaces at all - simply make the data structure completely immutable and manage a mutable reference to it where you happen to be using it to store state.

This means other objects in your system can't rely on a reference to the stats object - the stats object they have won't be the same stats object the player has after the player updates their stats.

This makes more sense anyway, since conceptually it's not really the stats that are changing, it's the player's current stats. Not the stats object. The reference to the stats object the player object has. So other parts of your system depending on that reference should all be explicitly referencing player.currentStats() or some such instead of getting ahold of the player's underlying stats object, storing it somewhere, and relying on it updating through mutations.

1

Wow... this really takes me back. I tried this same idea a few times. I was too stubborn to give up on it because I thought there would be something good to learn. I have seen others try this in code too. Most implementations I have seen called the Read-Only interfaces like yours FooViewer or ReadOnlyFoo and the Write-Only interface the FooEditor or WriteableFoo or in Java I think I saw FooMutator once. I'm not sure there is an official or even common vocabulary for doing things this way.

This never did anything useful for me in the places I tried it. I would avoid it altogether. I would do as others are suggesting and take a step back and consider whether you really need this notion in your bigger idea. I'm not sure there is a right way to do this since I never actually kept any of the code I produced while trying this. Each time I backed out after considerable effort and simplified things. And by that I mean something along the lines of what others said about YAGNI and KISS and DRY.

One possible downside: repetition. You will have to create these interfaces for a lot of classes potentially and even for just one class you have to name each method and describe each signature at least twice. Of all the things that burn me in coding, having to change multiple files in this way gets me in the most trouble. I eventually end up forgetting to make the changes in one place or the other. Once you have a class called Foo and interfaces called FooViewer and FooEditor, if you decide it would be better if you called it Bar instead you have to refactor->rename thrice unless you have a really amazingly smart IDE. I even found this to be the case when I had a considerable amount of the code coming from a code generator.

Personally, I dislike creating interfaces for things that have only a single implementation too. It means when I travel around in the code I can't just jump to the only implementation directly, I have to jump to the interface and then to the implementation of the interface or at least press a few extra keys to get there. Those things add up.

And then there is the complication you mentioned. I doubt I'll ever go this particular way with my code again. Even for the place this fit best into my overall plan for the code (an ORM I built) I pulled this and replaced it with something easier to code with.

I would really give more thought to the composition you mentioned. I'm curious why you feel that would be a bad idea. I would have expected to have EffectiveStats compose and immutable Stats and something like StatModifiers which would further compose some set of StatModifiers that represent whatever might modify stats (temporary effects, items, location in some enhancement area, fatigue) but that your EffectiveStats would not need to understand because StatModifiers would manage what those things were and how much effect and what kind they would have on which stats. The StatModifier would be an interface for the different things and could know things like "am I in the zone", "when does the medicine wear off", etc... but would not have to even let any other objects know such things. It would only need to say which stat and how modified it was right now. Better yet StatModifier could simply expose a method for producing a new immutable Stats based on another Stats which would be different because it had been altered appropriately. You could then do something like currentStats = statModifier2.modify(statModifier1.modify(baseStats)) and all the Stats can be immutable. I wouldn't even code that directly, I would probably loop through all the modifiers and apply each to the result of the previous modifiers starting with baseStats.

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.