In this activity, we will start with a version of the calculator that
uses the State design pattern to implement operator precedence.
In this activity, we will follow these steps:
- Understand how the
Statedesign pattern is used to implement operator precedence - Refactor the code to introduce the
Observerdesign pattern. - Update the tests accordingly and run them to make sure that they still pass.
- Refactor code to remove duplication/inefficiencies.
To enforce some consistency across implementations (and allow us to build on the same code in future activities), we will start with a Calculator that implements operator precedence using the State design pattern.
The solution was briefly reviewed during today's class, and the design pattern involves the following files:
- File
calculator-state.interface.tsdeclares an interfaceICalculatorStatethat defines the functionality of the different states - File
abstract-calculator-state.tsdefines an abstract classAbstractCalculatorStatethat implementsICalculatorState. This class contains functionality that is common to all states, and it maintains a reference to the context (i.e., a reference to an instance ofCalculatorModel) - Files
entering-first-number-state.ts,entering-second-number-state.ts, andentering-third-number-state.tsdefine states corresponding to the state machine that was shown in class. The methods in these classes (e.g.digitandbinaryOperatordefine the state-specific behaviors of the corresponding operations) - Class
CalculatorModelinitializes the state machine and delegates all operations to it. It defines a methodchangeStatethat is invoked by the various concrete states when a state transition is required.
To begin the activity, run npm install in the root of the starter code (location where package.json is present).
Once the installation is complete, run the test commands npm run test. You should see the output as below:
$ npm run test
> week-6-StructuralBehavioral-DP@1.0.0 test
> node scripts/jest.js
PASS src/models/calculator.model.spec.ts
CalculatorModel
✓ should contain a CalculatorModel class that implements ICalculatorModel (1 ms)
✓ should display `13` when equals is clicked on `7 + 6` (1 ms)
✓ should display `5` when equals is clicked on `15 - 10` (1 ms)
✓ should display `21` when equals is clicked on `3 * 7` (1 ms)
✓ should display `12` when equals is clicked on `144 / 12` (1 ms)
✓ should display `14` when equals is clicked on `2 + 3 * 4`
✓ should return `90` when equals is clicked on `100 + 200 / 10 - 3 * 10`
✓ should display `82` when equals is clicked on `100 + 1 - 8 * 1 * 3 / 4 + 7 - 10 / 2 * 4
PASS src/index.spec.ts
week6-structural-behavioral-DP
CalculatorModel
✓ CalculatorModel exists
Test Suites: 2 passed, 2 total
Tests: 9 passed, 9 total
Snapshots: 0 total
Time: 0.453 s, estimated 1 s
Ran all test suites.- please study the code in the classes mentioned above, and make sure that you understand what roles they play in the
Statedesign pattern
We begin by defining our observer. This involves the following steps:
-
Define an interface
ICalculatorObserverthat declares a methodupdate(message: string): void
-
Define a class
CalculatorObserverthat implementsICalculatorObserverby defining the update method so that it prints the message to standard output
Next, modify the class CalculatorModel, which is the subject in the Observer design pattern by
- maintaining an array of
ICalculatorObservers - adding methods
attachanddetachfor adding and removing observers
-
now, define a method
public notify(message: string): void
in class CalculatorModel that notifies all of the observers of the message
- note: the
notifymethod also needs to be declared in the interfaceIContextthat is used by the concrete state classes to refer to theCalculatorModel
- modify the code so that
notifyis invoked whenever the calculator performs a calculation
- modify the last test in file
calculator.model.spec.tsso that it uses the observer - confirm that observer prints messages as expected
In the root directory, run the command npm run zip. This command will generate a zip file called submission.zip. Upload the submission.zip file to Gradescope and tag your partner on Gradescope on the submission.