4

So i created a simple visitor pattern for liquids. Such as Milk,juice and liquor.

a Milk class can look like this:

public class Milk implements Visitable{

public float tax=0;

@Override
public int price() {
    return 4;
}

@Override
public void accept(Visitor visitor) {
    visitor.visit(this);
}
}

//and likewise for juice and liquor

and then we can have a visitor which looks like this to do the work of calculating taxes :

public class TaxVisitor implements Visitor {

double taxPerentLiquor=.18;
double taxPerentMilk=.15;
double taxPerentJuice=.10;


@Override
public void visit(Liquor liquor) {

    int price =liquor.price();
    liquor.setTax((float) (price*taxPerentLiquor));
}

@Override
public void visit(Milk milk) {
    float price =milk.price();
    milk.setTax((float) (price*taxPerentMilk));


}

@Override
public void visit(Juice juice) {

    int price =juice.price();
    juice.setTax((float) (price*taxPerentJuice));

}

}

and when we are using it we would do this:

Visitor taxVisitor = new TaxVisitor();

    Milk milk = new Milk();
    Juice juice = new Juice();

    milk.accept(taxVisitor);
    juice.accept(taxVisitor);

and a "visitor" would calculate the tax for each liquid for me. and i see the benefit that i do not have to modify the collection of objects themselves when i want to add a new calculation. But my trouble comes when i want to add a new method. Following open closed principle i should not be adding new methods to the milk class for example. But let us imagine i want to add functionality to know what color the liquid is. So i'd like to extend the milk object to have a method called "getColor()" which would return "#FFFFFF". This seems like i would need a decorator pattern to add this functionality if i want to follow SOLID open closed principle. Is there a way to do this with visitor pattern ?

j2emanue
  • 60,549
  • 65
  • 286
  • 456
  • Unless you plan on storing the color of every liquid in the visitor (as you've done with tax), no. Visitor doesn't add behavior to an object, it performs behavior on an object. As with your tax visitor, you are not adding behavior to the objects, you are simply performing behavior based on the type of the object (which may trigger the object to perform *already existing* behavior) – Vince Oct 05 '16 at 21:11
  • I see. so visitor is for executing behavior not adding behavior then. – j2emanue Oct 05 '16 at 22:13
  • Technically, it does add behavior, but not to the element. The behaviors belong to a *family* of elements, not just one. The problem with applying that to your situation is expressed by your `TaxVisitor`: the visitor would need to maintain any state not maintained by the elements. Hence, if none of the liquids maintained their own color, a `ColorVisitor` would have to maintain all of them. It's possible, but not practical. – Vince Oct 05 '16 at 23:00
  • What pattern do you recommend if i want to add methods (add element behavior) to my collection of elements without modifying them? – j2emanue Oct 05 '16 at 23:20
  • Adding a `getColor()` to your liquids **does not** violate O/C. That's the purpose of "*open for extension*": you can still add behaviors, as long as they don't affect any existing behavior. If you affect existing code, any code that depends on it may also be affected, which could potentially break contracts (thus break software). If adding `getColor()` doesn't affect any working code, then it's not a principle violation, it's scaling up. [Look at the first bullet point from this link](https://en.wikipedia.org/wiki/Open/closed_principle#Meyer.27s_open.2Fclosed_principle) – Vince Oct 05 '16 at 23:25
  • Feel free to start a chat, we can discuss this further if you'd like – Vince Oct 05 '16 at 23:30
  • your totally right. i see it now. open for extension is not the same as closed for modification. got it !! if you feel like posting an official answer you may. – j2emanue Oct 05 '16 at 23:33

1 Answers1

3

Add the function, you aren't violating O/C

You won't violate O/C by adding a new function, as long as that function does not affect any existing code. You'll be perfectly fine adding a getColor() to your liquids.

O/C is about preventing modification of existing code

The purpose of O/C is to avoid contract violations. When a developer writes software, they'll specify exactly how the software should and should not work. This is the contract.

When writing code that other developers depend on, you cannot be sure what contracts those developers have set in place. All you know about is the contract you defined for your code. Developers trust that you will not modify your contract in a way that modifies theirs.

Since you cannot be sure whether changing your contract will affect other developers' contracts, any code of yours that other developers use must be "CLOSED for modification", or you risk contract violations.

O/C doesn't prevent you from scaling up

Adding code that has no effect on existing code doesn't carry risk of interfering with other developers' contracts.

If you add some functions, no one is forcing clients to use those new functions in their systems; they don't need to worry of any potential risks, or how they even work, if they won't be in use. Classes are "OPEN for extension", as it won't cause any potential harm to existing clients.

Community
  • 1
  • 1
Vince
  • 14,470
  • 7
  • 39
  • 84