ChuckJ's Blog Please read my disclaimer. It is better to be silent and thought a fool, than to speak and remove all doubt." -- Silvan Engel

...Removing All Doubt

Chuck Jazdzewski

The Expression Problem: Part IV - Layers

So far I have presented three different approaches to solving the expression problem, procedural, pure object-oriented, and a visitor pattern. This time I will present a technique I will refer to as layers. Like the visitor pattern, it is a modification of the pure object-oriented approach that takes advantage of partial classes. The solution begins very similarly to the other object-oriented approaches, I create an abstract class to represent an abstraction of expressions,

    abstract partial class Expr { }

Notice the addition of the partial modifier. I will take advantage of that shortly. For now I will just flush out the rest of the hierarchy in a way that should, by now, be very familiar. I created a class to represent literals,

    partial class Literal: Expr {
        protected double Value;

        public Literal(double value) { Value = value; }
    }

Notice that, as with the pure object-oriented approach, I can keep the field used to hold the value as protected. This is also true for the class to represent an abstraction of binary operators.

    abstract partial class BinaryOperator: Expr {
        protected Expr Left;
        protected Expr Right;

        public BinaryOperator(Expr left, Expr right) {
            Left = left;
            Right = right;
        }
    }

The actual binary operators are just descendants of BinaryOperator with no additional data.

    partial class Add: BinaryOperator {
        public Add(Expr left, Expr right) : base(left, right) { }
    }

    partial class Subtract: BinaryOperator {
        public Subtract(Expr left, Expr right) : base(left, right) { }
    }

    partial class Multiply: BinaryOperator {
        public Multiply(Expr left, Expr right) : base(left, right) { }
    }

    partial class Divide: BinaryOperator {
        public Divide(Expr left, Expr right) : base(left, right) { }
    }

So far, this is nearly identical to the pure object-oriented approach. Where it differs is in how new operations are added.  To add the Evaluate() method, instead of modifying each class to add the methods, I can take advantage of the fact I left them as partial classes and create additional parts that contain the methods instead. First I created a new part of the Expr class to introduce the virtual method Evaluate().

    abstract partial class Expr {
        public abstract double Evaluate();
    }

Compiling now will generate several errors indicating that the other classes do not implement the Evaluate() method.  This is want I wanted. Using abstract in the above class part lets the compiler help me determine when I have completed the implementation of the evaluate operation. The rest of the class parts for Evaluate() looks like,

    partial class Literal {
        public override double Evaluate() {
            return Value;
        }
    }

    abstract partial class BinaryOperator {
        public sealed override double Evaluate() {
            return EvaluateOp(Left.Evaluate(), Right.Evaluate());
        }

        protected abstract double EvaluateOp(double left, double right);
    }

    partial class Add {
        protected override double EvaluateOp(double left, double right) {
            return left + right;
        }
    }

    partial class Subtract {
        protected override double EvaluateOp(double left, double right) {
            return left - right;
        }
    }

    partial class Multiply {
        protected override double EvaluateOp(double left, double right) {
            return left * right;
        }
    }

    partial class Divide {
        protected override double EvaluateOp(double left, double right) {
            return left / right;
        }
    }

I call each of the collections of class parts a layer. You can think of them much like a transparency teachers uses on overhead projectors. You can build up a class by combining layers of functionality. I usually keep each layer in its own file named for the layer. In this case, Evaluator.cs might be a good choice.

To demonstrate building up a class with through layers I will create a printing layer.

    abstract partial class Expr {
        public abstract void Print();
    }

    partial class Literal {
        public override void Print() {
            Console.Write(Value);
        }
    }

    abstract partial class BinaryOperator {
        public sealed override void Print() {
            Left.Print();
            PrintOp();
            Right.Print();
        }

        protected abstract void PrintOp();
    }

    partial class Add {
        protected override void PrintOp() {
            Console.Write(" + ");
        }
    }

    partial class Subtract {
        protected override void PrintOp() {
            Console.Write(" - ");
        }
    }

    partial class Multiply {
        protected override void PrintOp() {
            Console.Write(" * ");
        }
    }

    partial class Divide {
        protected override void PrintOp() {
            Console.Write(" / ");
        }
    }

As in the other approaches, I will demonstrate how to add support for the Power expression form. I have two choices, I can add it just as we did in the pure object-oriented approach,

    class Power: BinaryExpr {
        public Power(Expr left, Expr right) : base(left, right) { }

        protected override double EvaluateOp(double left, double right) {
            return Math.Pow(left, right);
        }

        protected override void PrintOp() {
            Console.Write(" ^ ");
        }
    }

or I could divide it into multiple parts that can be co-located with the other similar parts. The data part would look like,

    partial class Power: BinaryExpr {
        public Power(Expr left, Expr right) : base(left, right) { }
    }

The part for the expression layer would look like,

    partial class Power: BinaryExpr {
        protected override double EvaluateOp(double left, double right) {
            return Math.Pow(left, right);
        }
    }

An the printing layer part would look like,

    partial class Power: BinaryExpr {
        protected override void PrintOp() {
            Console.Write(" ^ ");
        }
    }

It is this flexibility that makes layers so appealing. You can decide, case by case, which is the right way to modify the hierarchy.

12/03/2005 5:48 PM | Comments [1] | #Programming

Content © 2008 Chuck Jazdzewski | Subscribe to my RSS feed.

All source code above is usable under the Microsoft Permissive License (Ms-PL) unless otherwise specified in the containing entry.

Powered by BlogX