Template Method Pattern in Software Design: A Case Study on Expressions, Study Guides, Projects, Research of Computer Science

The implementation of the template method pattern in a software design project, specifically in the context of expressions. The motivation behind using the pattern, the implementation details, and the benefits. The case study involves the development of a binaryexpr class and its derived classes for sum, product, and difference expressions.

Typology: Study Guides, Projects, Research

Pre 2010

Uploaded on 07/28/2009

koofers-user-cws
koofers-user-cws 🇺🇸

3.5

(3)

10 documents

1 / 25

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Software Design
CSE 335, Fall 2004
The Template-Method Pattern
Stephen Wagner
Michigan State University
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19

Partial preview of the text

Download Template Method Pattern in Software Design: A Case Study on Expressions and more Study Guides, Projects, Research Computer Science in PDF only on Docsity!

Software Design

CSE 335, Fall 2004

The Template-Method Pattern

Stephen Wagner

Michigan State University

Expressions

• In the project, Sum, Product and Difference are all very similar classes

• We developed a BinaryExpr class and derived all the binary expressions from

it.

class BinaryExpr : public Expr { protected: const Expr* left; const Expr* right; BinaryExpr( const Expr* l, const Expr *r) : leftOperand(l), rightOperand(r) {} };

• evaluate looks nearly identical in all the classes derived from BinaryExpr

First Factoring Attempt

• In all cases we compute the left, then compute the right, and then perform an

operation.

• Can we move this to BinaryExpr?

int BinaryExpr::evaluate(int & v, const Store & st) { int leftval, rightval; if (left->evaluate(leftval,st) && right->evaluate(rightval,st)) { if (type==SUM) v=leftval+rightval; if (type==PRODUCT) v=leftval*rightval; if (type==DIFFERENCE) v=leftval-rightval; return true; } return false; }

A Better Approach

• Lots of if statements are ugly

int BinaryExpr::evaluate(int & v, const Store & st) { int leftval, rightval; if (left->evaluate(leftval,st) && right->evaluate(rightval,st)) { v=operation(leftval,rightval); return true; } return false; }

• How and where should operation be defined?

• Why is this better?

A Normal Monster

• Wanders around maze

• Looks for and reacts to opponents

• Bounces off of walls

• What if we want to add additional monsters?

A Different Monster

The Template-Method Pattern

• Motivation: we want to implement an algorithm, but the exact details of the

algorithm can change

– we want to be able to describe a skeleton or outline for the algorithm

– we want to be able to specialize the algorithm

• In the template-method pattern

– the code that describes the algorithm is defined in the base class

– the code that specializes the algorithm is defined in the derived classes

• This has nothing to do with C++ templates.

The Template Pattern

• The template pattern uses inheritance differently

• Traditional

– operations of derived classes are invoked directly

– operations defined in derived classes will make use of primitive operations

defined in base class

• Template Pattern

– operation of base class is invoked directly

– the base class calls methods in the derived class

– variations in the algorithm are achieved by using hooks

Expressions and the Template Pattern

• The basic algorithm is defined in the base class

– compute the left expression, compute the right expression, perform an

operation and return the result

int BinaryExpr::evaluate(int & v, const Store & st) { int leftval, rightval; if (left->evaluate(leftval,st) && right->evaluate(rightval,st)) { v=operation(leftval,rightval); return true; } return false; }

– operation is the hook

• Each derived class defines its own operation method

• BinaryExpr::evaluate will invoke the operation method of one of the

derived classes.

The Template-Method Pattern and simplify

• In the project simplify was very similar for all three of the binary expressions

– Each function was 30+ lines long

– Each had to worry about negative results

• Using the Template-Method pattern

– Most of the code can be put in once place

– The tricky bit involving negative results is in one place

– Additional operations could be easily added

const Expr * Product::simplify() const { int leftv, rightv; const Expr leftsimp=left->simplify(); const Expr rightsimp=right->simplify(); bool leftb=leftsimp->evaluate(leftv); bool rightb=rightsimp->evaluate(rightv); if (leftb && rightb) { // simplify constant expressions delete leftsimp; delete rightsimp; if (leftvrightv>=0) return new Literal(leftvrightv); else return new Negation(new Literal(-(leftv*rightv)); } else if (leftb && leftv==1) { delete leftsimp; return rightsimp; } else if (rightb && rightv==1) { delete rightsimp; return leftsimp; } if (leftb && leftv==0 || rightb && rightv==0) { delete leftsimp; delete rightsimp; return new Literal(0); } return new Product(l,r); }

Hook

• The first 10 lines are identical except for the operation performed.

const Expr * BinaryExpr::simplify() const { int leftv, rightv; const Expr * leftsimp=left->simplify(); const Expr * rightsimp=right->simplify(); bool leftb=leftsimp->evaluate(leftv); bool rightb=rightsimp->evaluate(rightv); if (leftb && rightb) { // simplify constant expressions delete leftsimp; delete rightsimp; int val=operation(leftv,rightv); if (val>=0) return new Literal(val); else return new Negation(new Literal(val)); }

Hook #2 and

• We have more flexibility if we allow for left identities and right identities

else if (leftb && leftidentity(leftv)) { delete leftsimp; return rightsimp; } else if (rightb && rightidentity(rightv)) { delete rightsimp; return leftsimp; }

Hook

• Allow for additional simplifications to be performed

const Expr * BinaryExpr::simplify() const { int leftv,rightv; const Expr * leftsimp=left->simplify(); const Expr * rightsimp=right->simplify(); bool leftb=leftsimp->evaluate(leftv); bool rightb=rightsimp->evaluate(rightv); if (leftb && rightb) { delete leftsimp; delete rightsimp; int val=operation(leftv,rightv); if (val>=0) return new Literal(val); else return new Negation(new Literal(val)); } else if (leftb && leftidentity(leftv)) { delete leftsimp; return rightsimp; } else if (rightb && rightidentity(rightv)) { delete rightsimp; return leftsimp; } else return othersimplify(leftsimp, rightsimp, leftb, rightb, leftv, rightv);