Software Architecture
Spring 2019
OOP: Encapsulation
Debrief: Code Reviews
Structure of the Day
Today is a talking theory day....
-
Design Principles
-
Encapsulation
-
Law of Demeter
-
Interfaces
-
Modules
https://slides.com/joelross/arch-sp19-encapsulation/live

Class
A Class organizes a set of data AND functionality associated with that data.
Classes act as blueprints/templates/recipes for Object instances.

A way of encoding components of the domain model


How do I organize my data and behavior into classes?
Design Principles
Design Principle
Model-in-Code Principle
"The source code should express the model"

Principle of
Separation of Concerns

"Organize software into separate components (pieces) that are as independent as possible."

Cohesion
"The degree to which elements of a module are related and belong together."
Coupling
"The degree to which software modules are inter-connected (and inter-dependent)."

Design Principle
Single Responsibility Principle
"A component should have a single responsibility"

CRC Cards

Keep these unique and not overlapping!
Class-Responsibility-Collaboration (CRC) Cards are a technique for discovering and organizing classes

Design Principle
Single Responsibility Principle
"A component should have one and only one reason to change"
"A component should have a single responsibility"
"A component should have a single responsibility"
Adapting to Change
-
It should be easy to locate the place where a change is needed
-
A change to a single artifact in a program should be made in just one place
-
A change to an artifact should require no (or minimal) changes to the rest of the program
-
An error introduced by the change should only affect the thing being changed
Design software so that it can be modified in the future!
Classes Provide
Abstraction
Abstraction is the process of generalization . It lets us work with higher-level representations rather than specific details.

Classes Provide
Encapsulation
Encapsulation
Encapsulation is the process of compart-mentalizing the elements of an abstraction. It hides the details in the abstraction.

Encapsulation Example
class Line {
constructor(
private startX: number,
private startY: number,
private endX: number,
private endY: number
) {}
move() { /*...*/ }
stretch() { /*...*/ }
changeColor() { /*...*/ }
}
//Use the class to work with the encapsulated data!
line: Line = new Line(1,2, 5,8);
line.move(); //move around
line.stretch(); //get longer
Information Hiding
Information Hiding is keeping encapsulated details inaccessible to other components.

class Location {
//what attributes?
private x: number; //x-coordinate
private y: number; //y-coordinate
private latitude: number;
private longitude: number;
private address: string = "1234 Main Street";
private place: string = "Alice's Restaurant";
//methods
distanceFrom(other:Location): number { /*...*/ }
areWeThereYet(): boolean { /*..*/ }
}
Information Hiding
The specific properties (data and functions) that an abstraction (class) makes available for interaction. The interface available to the public.

Public Interface
Access Modifiers
In order to keep information hidden (from the TypeScript compiler), we use access modifiers to encapsulated properties.
class Person {
//private variables are only accessible from inside the class
private password: string;
//public variables are accessible anywhere (default access)
public name: string;
//readonly vars are public, but can't be changed (like const)
readonly species: string = "Human"
//Use functions to provide (controlled) access to private fields
getPassword(): string { return this.password; }
setPassWord(newPass: string) { this.password = newPass; }
}
Encapsulation Example
class Line {
private startX:number;
private startY:number;
private endX:number;
private endY:number;
private length:number;
constructor(x1:number, y1:number, x2:number, y2:number) {
this.startX = x1; //etc
}
setStart(p:Point):void {
this.startX = p.getX();
this.startY = p.getY();
this.length = this.calculateLength();
}
getEnd():Point {
return new Point(this.endX, this.endY);
}
//...
}
Example from Pragmatic Programmer
Encapsulation Example
class Line {
private start:Point;
private end:Point;
private length:number;
constructor(x1:number, y1:number, x2:number, y2:number) {
this.start = new Point(x1, y1); //etc
}
setStart(p:Point):void {
this.start = p;
this.length = this.calculateLength();
}
getEnd():Point {
return this.end;
}
//...
}
Example from Pragmatic Programmer
public interface is the same--no change needed!
duplicate information
Encapsulation Example
class Line {
private start:Point;
private end:Point;
constructor(x1:number, y1:number, x2:number, y2:number) {
this.start = new Point(x1, y1); //etc
}
setStart(p:Point):void {
this.start = p;
}
getEnd():Point {
return this.end;
}
getLength():number {
return this.calculateLength();
}
}
Example from Pragmatic Programmer
Encapsulation enforces Abstraction
Design Principle
Encapsulate what varies
"Hide code that changes a lot behind a public interface that doesn't."
Design Principle
Principle of Least Knowledge
(Law of Demeter)
"A component should not know the internal details of another component"
"Talk only to your immediate friends,
not to friends of friends."
An Example: Payment
class Customer { //responsible for buying
private name: string;
private wallet: Wallet;
getName(): string { return this.name; }
getWallet(): Wallet { return this.wallet; }
}
class Wallet { //responsible for tracking money
private value: number;
getTotalMoney() :number { return this.value; }
subtractMoney(debit: number) { this.value -= debit; }
}
class Merchant { //responsible for selling
sellTo(myCustomer: Customer) {
let cost = 5.00;
let theWallet: Wallet = myCustomer.getWallet();
if(theWallet.getTotalMoney() >= cost){
theWallet.subtractMoney(cost);
this.revenue += cost;
}
else { /* do not sell */ }
}
}
An Example: Payment
class Customer { //responsible for buying
private wallet: Wallet;
getPayment(bill: number): number {
if(this.wallet.getTotalMoney() >= bill) {
this.wallet.subtractMoney(bill);
return bill;
}
else { return 0; } //treat as error code ?
}
}
class Wallet { //responsible for tracking money
private value: number;
getTotalMoney(): number { return this.value; }
subtractMoney(debit: number) { this.value -= debit; }
}
class Merchant { //responsible for selling
sellTo(myCustomer: Customer) {
let cost = 5.00;
let paidAmount = myCustomer.getPayment(cost);
if(paidAmount > 0) {
this.revenue += paidAmount;
}
else { /* do not sell */ }
}
}
Law of Demeter (Methods)
A method of an object should only call methods on:
(a) the object itself
(b) the method's parameters
(c) any objects the method instantiates
(d) any direct component objects
class Dog {
private mFlea: Flea; //instance variable
bark() { } //member function
//Inside this method we can call functions on:
fetch(aBall: Ball) {
this.bark(); //1. the object itself
aBall.squeeze(); //2. method parameters
let wag = new TailWag();
wag.show(); //3. instantiated objects
mFlea.scratch(); //4. direct component objects
}
}
Law of Demeter (Methods)
Law of Demeter Practice
exercise from Pragmatic Programmer
Does this code obey the Law of Demeter?
function showBalance(account: BankAccount):void {
let amount: Money = account.getBalance();
console.log(amount.printFormat());
}
function processTransaction(account: BankAccount, value: number) {
Money amount = new Money();
amount.setValue(123.45);
account.setBalance(amount);
let who: Person = account.getOwner();
console.log("Updated account for " + who.getName());
}
class Colada {
private blender: Blender;
private stuff: IngredientList;
constructor() {
this.blender = new Blender();
this.stuff = new IngredientList();
}
mix() {
this.blender.addIngredients(this.stuff.getIngredients());
this.blender.blend();
}
}

Design Principles
-
Single Responsibility Principle
- This leads to good cohesion
- This leads to good cohesion
-
Principle of Least Knowledge (Law of Demeter)
- This leads to good coupling
Class Relationships
Classes have different relationships:
-
Dependency: class A uses a class B
-
Composition: class A has a class B
-
Realization: class A is a class B



Types
A value's type indicates what that value is.
// age IS A number
let age: number = 23
// firstName IS A string
let firstName: string = "Ada"
//an interface for a `Person` type
interface Person {
firstName: string,
age: number
}
//ada IS A Person
let ada: Person = {firstName: "Ada", age: 33};
We use interfaces to specify custom types
The specific properties (data and functions) that an abstraction makes available for interaction.
Defines an explicit annotation (contract)
Programming Interfaces

TypeScript lets us make provided properties explicit, and will warn us if the contract is violated.
interface Computer {
add(x: number, y: number): number; //method signature
print(text: string): void; //no body
}
let hal: Computer = { /*...*/ };
hal.add(2, 3); //no problem!
hal.openThePodBayDoors(); //Error!
Programming Interfaces
Classes Specify Interfaces
An object's class specifies its type, and thus its interface (an is a relationship)!
class Dog {
constructor(private name: string) {}
bark() { /*...*/ }
beg() { /*...*/ }
}
//fido IS A Dog
let fido: Dog = new Dog("Fido");
fido.bark();
fido.beg();
fido.rollOver(); //Error! Not in the interface!
Defining a class also defines an interface!
Polymorphism
An object can simultaneously have multiple different types (from the Greek: many forms).
interface VisaAcceptor {
payWithVisa(cardNum: number): Receipt;
}
//explicit: elements of this class fulfill the interface
//a CoffeeShop IS A VisaAcceptor
class CoffeeShop implements VisaAcceptor {
payWithVisa(cardNum: number): Receipt { /* ... */ }
}
//object IS A CoffeeShop
let shop: CoffeeShop = new CoffeeShop();
//object also IS A VisaAcceptor
let payee: VisaAcceptor = shop; //assign same object
(The arrow points at the interface)

<<interface>>
Barker
Dog
Realization (dotted line, hollow arrow) represents a "is a" relationship--a class fulfills a contract or interface
UML Class Relationships
Why is this useful?

Polymorphism
Interacting with classes in terms of a shared interface lets us work at a higher level of abstraction.
interface VisaAcceptor {
//takes in the card number, returns Receipt object
payWithVisa(cardNum: number): Receipt;
}
class CoffeeShop implements VisaAcceptor { /* ... */ }
class CharityDrive implements VisaAcceptor { /* ... */ }
let shop: CoffeeShop = new CoffeeShop();
let charity: CharityDrive = new CharityDrive();
shop.payWithVisa();
charity.payWithVisa();
interface VisaAcceptor {
//takes in the card number, returns Receipt object
payWithVisa(cardNum: number): Receipt;
}
class CoffeeShop implements VisaAcceptor { /* ... */ }
class CharityDrive implements VisaAcceptor { /* ... */ }
let shop: VisaAcceptor = new CoffeeShop();
let charity: VisaAcceptor = new CharityDrive();
shop.payWithVisa();
charity.payWithVisa();
VisaAcceptor box contains
Shop object
interface VisaAcceptor {
//takes in the card number, returns Receipt object
payWithVisa(cardNum: number): Receipt;
}
class CoffeeShop implements VisaAcceptor { /* ... */ }
class CharityDrive implements VisaAcceptor { /* ... */ }
let shop: VisaAcceptor = new CoffeeShop();
let charity: VisaAcceptor = new CharityDrive();
//put them in an array together (same type!)
let placesToPay: VisaAcceptor[] = [shop, charity, ...];
//Abstract away their differences and loop through them all!
placesToPay.forEach((place: VisaAcceptor) => {
place.payWithVisa();
}
Variables vs. Values
An object can be stored in a variable for any type it is, but the value still retains it's original type(s) and code.
interface Barker {
bark(): void;
}
class Dog implements Barker {
bark() {
console.log("Bark!");
}
wagTail() {
console.log("Wag tail!");
}
}
let animal: Barker = new Dog();
animal.bark(); //works!
animal.wagTail(); //TS compiler error, but will run
//Will not compile in Java
//explicitly assert `animal` is a `Dog` (typecast)
(animal as Dog).wagTail(); //compiles
Interface Inference
Because JavaScript is dynamically typed, any class that fulfills an interface is treated as implementing that interface.
Interfaces define a set of "minimal" properties
interface Barker {
bark(): void;
}
class Dog implements Barker {
bark() {
console.log("Bark!");
}
}
//Dog fulfills the interface,
//so IS a Barker!
let animal: Barker = new Dog();
animal.bark(); //works!
interface Barker {
bark(): void;
}
class Dog {
bark() {
console.log("Bark!");
}
}
//Dog fulfills the interface,
//so IS a Barker!
let animal: Barker = new Dog();
animal.bark(); //works!
interface Barker {
bark(): void;
}
class Dog {
bark() {
console.log("Bark!");
}
}
//Dog fulfills the interface,
//so IS a Barker!
let animal: Barker = new Dog();
animal.bark(); //works!
//does not apply to object literals (excess property checking)
let pet: Barker = { bark:() => {}, yip:() => {} } //Error
Duck Typing

When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.

Interface Practice!
Define a class
HourlyEmployee
who has a
name, an
hourly wage, and a
number of hours worked this week.
Also define a class
SalariedEmployee
who has a
name and an
annual (52-week) salary. Both classes should include methods that return their
pay for that week.
Define an
interface that both of these classes fulfill.
Use that interface to create an array that includes
two
HourlyEmployee
objects and
two
SalariedEmployee
objects. Use a loop to print out the salaries of all of the employees.
How do interfaces help with abstraction?
How do interfaces help with encapsulation?
Design Principle
Program to interfaces, not to implementations
Modules
In TypeScript (as well as in Node and ES6), each file is treated as a separate module of code.
Values (including functions) are only available within their module, but can be explicitly exported (shared) and imported (loaded)
TypeScript Modules
Modules provide additional abstraction and encapsulation!
Importing Modules
It is possible to import external libraries and make them available in your script.
CommonJS (used by Node.js)
ES 6 Modules (used by Browsers and TypeScript )
const util = require('util'); //can only import a specific value
import { Random, Widget } from 'util'; //import specific values
import random from 'util'; //import "default" value
import * as util from 'util'; //import all values into single obj
//(would use `util.Random()`)

Exporting Variables
To make a file into a module,
export
variables, functions, and classes to be available for
import
.
/*** my-module.js ***/
export function foo() { return 'foo'; } //named export
function bar() { return 'bar'; }
export bar; //export previously defined variable
//will not be available (a "private" function)
function baz() { return 'baz'; }
/*** index.js ***/
import {foo, barFunction} from './my-module'; //import multiple
foo() //=> 'foo'
barFunction() //=> 'bar'
//import everything that was exported
//loads as a single object with values as properties
import * as theModule from './my-module'; //import everything
theModule.foo(); //=> 'foo'
theModule.barFunction(); //=> 'bar'
theModule.baz(); //Error [private function]
encapsulation!
Default Exports
Each module can have a single default export, which provides a shortcut when importing.
/*** my-module.js ***/
export default function sayHello() {
return 'Hello world!';
}
/*** index.js ***/
//default import can assign own alias without needing name!
import greet from './my-module';
greet(); //=> "Hello world!"
Align Modules and Components
Make the modules (parts of the code: files, classes, etc) correspond to components (parts of the model/system)

- Organize module files by component
- Put related code in related modules (cohesion!)
Packages in UML
It is also possible to indicate modules (called packages) in UML diagrams, drawn as a "folder" containing the relevant classes.

Summary
New Design Principles
-
Single Responsibility Principle
-
Encapsulate what varies
-
Principle of Least Knowledge (Law of Demeter)
-
Program to interfaces, not to implementations
ACTION ITEMS!
For Next Time
-
Start on Homework 2 (Due week from Monday)
- Ask for help if you have questions or get stuck!
-
Reading: TBD
- Will send out an email once I've decided on a reading (if any)
arch-sp19-encapsulation
By Joel Ross
arch-sp19-encapsulation
- 887