SE322 · Chapter 5 · Lecture 2 · Annotated SlidesPrinciples of Detailed Design II: Component Design, SRP, OCP and LSP
Chapter 5 Quiz PDF
Overview

Principles of Detailed Design II: Component Design, SRP, OCP and LSP

Architecture names the components; component design decides what goes inside them. This lecture shows how a component becomes classes and interfaces, refreshes the four object-oriented ideas you need for that (classes, abstract classes and interfaces, inheritance, polymorphism), and then covers the first three SOLID principles. Each principle is a test you can run on a class diagram.

Component design

Defines the internal structure (classes, data) and behavior (algorithms, interactions) of each architectural component. In OO it is drawn as class diagrams.

SRP: Single Responsibility

A class should have one reason to change, meaning one actor or policy drives its changes. It is about responsibilities, not about having one method.

OCP: Open-Closed

Add new behavior by adding new classes behind a stable abstraction, not by editing working code.

LSP: Liskov Substitution

A subclass must be usable anywhere its parent is expected without breaking what callers rely on.

How the lecture is organized

  • Slides 1–6Component design: from architecture components to classes and interfaces
  • Slides 7–14OOP refresher: abstract classes, interfaces, inheritance, polymorphism (Java and UML)
  • Slides 15–21SOLID intro and SRP: Person/Email, Employee, payroll exercise
  • Slides 22–31OCP: the game engine redesign and the salary exercise
  • Slides 32–38LSP: flying “terrestrial” character, square/rectangle, License, fruit exercise
  • Slide 39What comes next: ISP, DIP and internal behavior

Exam focus

Confirmed: Final 252 Q5 (5 marks) asked you to name the SRP violation in a BookManager class and redraw it, then draw an OCP class diagram so E-books can be added (see slides 21 and 31). LSP has not appeared on a past paper here, so the LSP questions on this page are predictions.

How to read this page

Every original slide is shown unchanged. Yellow marks on a slide point to a numbered note: hover or tap a mark to jump to its note, and hover a note to see its marks. Questions tagged Past exam appeared on a real paper; Prediction questions are likely angles, not confirmed.

All 39 slides
  1. 1 Software Design and Architecture: Principles of Detailed Design
  2. 2 Lecture Outlines
  3. 3 Overview of Component Design
  4. 4 Overview of Component Design
  5. 5 Overview of Component Design
  6. 6 Overview of Component Design
  7. 7 What Is Abstract Class?
  8. 8 What is Interface?
  9. 9 Further reading
  10. 10 Refresh your memory … OOP concepts (Interface)
  11. 11 Refresh your memory … OOP concepts (Abstract Classes)
  12. 12 Refresh your memory … OOP concepts (Inheritance)
  13. 13 Refresh your memory … OOP concepts (Inheritance)
  14. 14 Refresh your memory … OOP concepts (Polymorphism )
  15. 15 Design Principles of Internal Component Design
  16. 16 1- Single Responsibility Principle (SRP)
  17. 17 SRP - example
  18. 18 SRP - example
  19. 19 SRP: does every function need its own class?
  20. 20 SRP Applies to Responsibilities, Not Methods
  21. 21 In Class Exercises
  22. 22 2- The Open-Closed Principle (OCP)
  23. 23 The Open-Close Principle
  24. 24 The Open-Close Principle: the add() code
  25. 25 The Open-Close Principle (OCP)
  26. 26 The Open-Closed Principle (OCP): first attempt
  27. 27 The Open-Closed Principle (OCP): GameEngine with type checks
  28. 28 The Open-Closed Principle (OCP): one move() for all
  29. 29 The Open-Closed Principle (OCP): the redesign
  30. 30 The Open-Closed Principle (OCP)
  31. 31 Exercise
  32. 32 3- The Liskov Substitution Principle (LSP)
  33. 33 The Liskov Substitution Principle (LSP)
  34. 34 The Liskov Substitution Principle (LSP) - example
  35. 35 The Liskov Substitution Principle (LSP) – example 2
  36. 36 The Liskov Substitution Principle (LSP) – example 3
  37. 37 LSP: a sanity check
  38. 38 Exercise
  39. 39 Lecture Outlines
Slide 1 / 39

Software Design and Architecture: Principles of Detailed Design

Slide 1: Software Design and Architecture: Principles of Detailed Design

Notes

  1. This is the second lecture of Chapter 5. Lecture 1 described the detailed design process; this one is about the principles that make a detailed design good: easy to change, reuse and test.

Slide 2 / 39

Lecture Outlines

Slide 2: Lecture Outlines

Notes

  1. Internal structure means which classes and interfaces a component contains and how they relate. It is drawn with class diagrams.

  2. SOLID is five principles. Only the first three (S, O, L) are taught in this lecture; slide 39 moves I and D to the next one.

  3. Internal behavior (what the classes do at run time, drawn with sequence or state diagrams) is also left for the next lecture.

Slide 3 / 39

Overview of Component Design

Slide 3: Overview of Component Design

Notes

  1. Component design is the part of detailed design that opens each component up. Structure is what it is made of (classes, attributes, interfaces); behavior is how those parts act to deliver the component’s services.

  2. It does not invent new top-level components. It refines the ones the architect already chose, so every class you design belongs to one architectural component.

  3. In an object-oriented system each component’s inside is usually shown with one or more class diagrams. A large component may need several.

Slide 4 / 39

Overview of Component Design

Slide 4: Overview of Component Design

Notes

  1. How to read the top diagram (architecture view):

    ClientSystem, ServerSystem
    UML components: boxes with the small component icon. They are black boxes at this stage.
    Ball-and-socket on ServerInterface
    The circle (ball) is the interface ServerSystem provides; the half-circle (socket) is what ClientSystem requires. Joined, they mean the client uses that service.
  2. The bottom diagram is the same two components after detailed design: they are opened up to show the classes inside.

    Client
    A class inside ClientSystem.
    «interface» ServerInterface
    The provided interface, now an actual interface type inside ServerSystem.
  3. The dashed arrow with an open arrowhead from Client to ServerInterface is a dependency: Client calls the interface and never names the concrete class.

  4. The dashed line with a hollow triangle from ConcreteServer to ServerInterface is realization: ConcreteServer implements the interface. Another class could replace it and Client would not change.

  5. The takeaway box says it in one line: in detailed design you stop thinking in components and start thinking in classes, interfaces and types.

Slide 5 / 39

Overview of Component Design

Slide 5: Overview of Component Design

Notes

  1. Four things are decided in component design. Data structures say how information is stored. Algorithms say how results are computed. Interface details are the exact method names, parameters and return types. Communication mechanisms say how components talk: direct calls, messages or events.

  2. That makes component design the place where functional correctness is won or lost. Architecture can be perfect, but if the algorithm or the data passed between classes is wrong, the system still gives wrong answers.

  3. In the payment example the three classes must agree on details like the order of operations (discount before tax?) and the number format. Only then is the total right for every valid input. Those agreements are exactly what component design writes down.

Slide 6 / 39

Overview of Component Design

Slide 6: Overview of Component Design

Notes

  1. Because component design fixes the actual logic, it is a major factor in whether the system does what users need.

  2. The four object-oriented ideas a component designer must be fluent in. Slides 7–14 refresh each one:

    Class / object
    A class is the blueprint; an object is one instance created from it at run time.
    Abstract class / interface
    Types that define a contract other classes must complete or implement.
    Inheritance
    A subclass reuses and specializes a parent (“is-a”).
    Polymorphism
    One call, such as animal.sound(), runs different code depending on the actual object.
Slide 7 / 39

What Is Abstract Class?

Slide 7: What Is Abstract Class?

Notes

  1. An abstract class is declared with the abstract keyword. It can mix abstract methods (a signature with no body) and concrete methods (normal methods with code). The slide’s definition requires at least one abstract method. Java itself also accepts an abstract class with none, but answer with the slide’s definition in the exam.

  2. Any concrete (non-abstract) subclass must give a body to every abstract method it inherits. If it doesn’t, it must be declared abstract too.

  3. “Cannot be instantiated” means new Animal() is a compile error when Animal is abstract. You can still declare a variable of that type: Animal a = new Cat();.

  4. The four reasons all come down to sharing: common code lives once in the parent (default functionality, reuse), and the abstract methods fix a common set of operations every subclass must offer (template, common interface).

  5. Reading the small diagram:

    Top box «abstract»
    The abstract class. It holds an abstract method (italic) and + concreteMethod(), which has a body.
    Arrow to ConcreteClass
    ConcreteClass derives from it and supplies + abstractMethod(). The slide draws the arrow downward; standard UML points a hollow triangle from the subclass up to the parent.

Exam angle

PredictionQ1

True or false: an abstract class can contain concrete methods, and it can be instantiated with new.

Show answer

Half true. It can contain concrete methods, but it can never be instantiated directly. Only a concrete subclass can be created.

Slide 8 / 39

What is Interface?

Slide 8: What is Interface?

Notes

  1. An interface is a pure contract: it lists method signatures and no code. The slide says no concrete methods at all. Modern Java also allows default and static methods, but for this course an interface has only abstract methods.

  2. You cannot write new AnimalInterface(). You create an object of a class that implements it and refer to it through the interface type.

  3. Interfaces hold no instance variables (no per-object state). The only fields allowed are constants: public static final.

  4. Benefits: loose coupling, because callers depend on the contract and not on a specific class. Dynamic method resolution: at run time Java runs the method of the actual object behind the interface. It also lets unrelated classes share a capability outside one inheritance tree, since a Java class can implement many interfaces but extend only one class.

Exam angle

PredictionQ1

Give two differences between an abstract class and an interface (as defined in this course).

Show answer

An abstract class can have concrete methods and instance variables; an interface has only abstract methods and constants. A class extends one abstract class (extends) but can implement many interfaces (implements). In UML an abstract class is shown in italics, an interface with «interface».

Slide 9 / 39

Further reading

Slide 9: Further reading

Notes

  1. Further reading on when to choose an interface or an abstract class in Java.

  2. Further reading on UML relationships. The #4 anchor jumps to the section on generalization and realization, the two arrows used on slides 10–12.

Slide 10 / 39

Refresh your memory … OOP concepts (Interface)

Slide 10: Refresh your memory … OOP concepts (Interface)

Notes

  1. The code, line by line:

    CodeWhat it does
    public interface AnimalInterface {Declares a contract named AnimalInterface.
    public void sound();A required method: signature only, ending in ;, with no body.
    public void drawe ();A second required method (spelled “drawe” on the slide).
    }End of the contract.
    public class Cat implements AnimalInterface{implements promises that Cat supplies a body for every method in the interface.
    public void sound(){ System.out.println("Miaw"); }Cat’s own version of sound(): prints Miaw.
    public void eat(){ System.out.println("Cat is eating fish!"); }Cat’s eat() body.
  2. Watch the mismatch: the interface asks for drawe(), but Cat provides eat(). As printed, Java would reject Cat (“does not override abstract method drawe()”). The UML on the right shows what was intended: the interface has sound() and eat().

  3. Reading the UML:

    «interface» Animal
    The stereotype in guillemets marks the box as an interface.
    Horse, Cat
    Classes that each provide + sound() and + eat().
    Dashed lines with a hollow triangle
    Realization: the triangle points at the interface each class implements.

Exam angle

PredictionQ1

Which UML notation shows that class Cat implements interface Animal?

Show answer

A dashed line from Cat to Animal ending in a hollow triangle at Animal (realization), with Animal marked «interface».

Slide 11 / 39

Refresh your memory … OOP concepts (Abstract Classes)

Slide 11: Refresh your memory … OOP concepts (Abstract Classes)

Notes

  1. The code, line by line:

    CodeWhat it does
    public abstract class Animal {An abstract class: it can’t be instantiated.
    public abstract void sound();Abstract method: no body, and every concrete subclass must implement it.
    public void eat() { System.out.println("Animal is eating.."); }Concrete method: inherited as-is unless a subclass overrides it.
    public class Cat extends Animal {extends = inherits from Animal (a Java class can extend only one class).
    public void sound(){ System.out.println("Miaw"); }Required: fills in the abstract sound().
    public void eat(){ System.out.println("Cat is eating fish!"); }Optional: overrides the inherited eat() with Cat’s version.
  2. In UML, italics carry meaning: the class name Animal in italics means abstract class, and sound() in italics means abstract operation. eat() is upright because it has a body.

  3. Notation trap: the slide calls the dashed arrows “Realization”. Standard UML uses realization (dashed line, hollow triangle) for implementing an interface, and generalization (solid line, hollow triangle) for extending a class, abstract or not. In an exam, draw extends with a solid line and say why.

Exam angle

PredictionQ1

A class Dog extends an abstract class Animal. Which UML line connects them, and how is Animal’s name written?

Show answer

A solid line with a hollow triangle pointing at Animal (generalization), and Animal’s name in italics because it is abstract.

Slide 12 / 39

Refresh your memory … OOP concepts (Inheritance)

Slide 12: Refresh your memory … OOP concepts (Inheritance)

Notes

  1. Here Animal is an ordinary (concrete) class, so every method already has a body:

    CodeWhat it does
    public class Animal {A normal class: it can be instantiated.
    public void sound(){ ..."Animal is making a sound"... }Default sound().
    public void eat(){ ..."Animal is eating.."... }Default eat().
    public class Horse extends Animal{Horse is-a Animal.
    @Override public void sound(){ ..."Neigh"... }@Override asks the compiler to check that a matching parent method exists, which catches typos.
    @Override public void eat(){ ..."Horse is eating hay!"... }Horse’s own eat().
    public class Cat extends Animal{ ...sound "Miaw", eat "Cat is eating fish!" }Cat overrides both methods the same way.
  2. Unlike slide 11, nothing forces the subclasses to override: they could inherit Animal’s versions unchanged. Overriding is a choice here, not a requirement.

  3. The UML shows generalization: solid lines meeting at one hollow triangle that points at the parent, Animal. Read it as “Horse is an Animal” and “Cat is an Animal”.

Slide 13 / 39

Refresh your memory … OOP concepts (Inheritance)

Slide 13: Refresh your memory … OOP concepts (Inheritance)

Notes

  1. The demo creates one object of each class and calls both methods:

    CodeWhat it does
    Animal a = new Animal(); a.sound(); a.eat();Prints “Animal is making a sound” then “Animal is eating..”.
    Horse h = new Horse(); h.sound(); h.eat();Prints “Neigh” then “Horse is eating hay!”.
    Cat c = new Cat(); c.sound(); c.eat();Prints “Miaw” then “Cat is eating fish!”.
  2. The output panel on the right confirms each object runs the version defined in its own class. Every variable here has the same type as its object, so this is plain inheritance. Slide 14 shows what changes when the types differ.

Slide 14 / 39

Refresh your memory … OOP concepts (Polymorphism )

Slide 14: Refresh your memory … OOP concepts (Polymorphism )

Notes

  1. The variables are declared as Animal but hold a Horse and a Cat:

    CodeWhat it does
    Animal ah = new Horse();Declared type Animal, actual object Horse. This is allowed because a Horse is an Animal.
    ah.sound(); ah.eat();Prints “Neigh” and “Horse is eating hay!”: the HORSE’s versions, not Animal’s.
    Animal ac = new Cat();Same idea with a Cat.
    ac.sound(); ac.eat();Prints “Miaw” and “Cat is eating fish!”.
  2. This is polymorphism (dynamic binding). The declared type (Animal) decides which methods you are allowed to call; the actual object (Horse or Cat) decides which code runs. The choice happens at run time.

  3. Why it matters for this lecture: OCP (slides 22–31) works because a GameEngine holding a Character reference can call move() and get each character’s own movement without knowing its class.

Exam angle

PredictionQ1

Animal x = new Cat(); x.sound(); What is printed, and what is this mechanism called?

Show answer

“Miaw”. It is polymorphism (dynamic binding): the overriding method of the actual object runs, even though the variable’s type is Animal.

Slide 15 / 39

Design Principles of Internal Component Design

Slide 15: Design Principles of Internal Component Design

Notes

  1. The goal of every principle that follows: designs that are less complex, reusable, easier to modify and easier to maintain.

  2. SOLID is the acronym for the five principles: Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, Dependency Inversion. S, O and L are covered in this lecture.

Slide 16 / 39

1- Single Responsibility Principle (SRP)

Slide 16: 1- Single Responsibility Principle (SRP)

Notes

  1. The common misreading: “every module should do just one thing.” That is good advice for small functions, but it is not what SRP says.

  2. SRP is about who causes change. A module should answer to one actor (a user group, stakeholder or policy). Equivalently, a class has one reason to change. If the accountants and the IT team could each force edits to the same class, it has two responsibilities.

Exam angle

PredictionQ1

State the Single Responsibility Principle and explain what “reason to change” means.

Show answer

A class should have only one reason to change, i.e. it should be responsible to one actor or stakeholder. A reason to change is a source of change requests: a policy, user group or concern (data format, tax rules, report layout). Two independent sources mean two responsibilities that should be separated.

Slide 17 / 39

SRP - example

Slide 17: SRP - example

Notes

  1. The code (TypeScript-style), line by line:

    CodeWhat it does
    class Person {Represents a person.
    public name : string; public surname : string; public email : string;Person data: three text fields.
    constructor(name, surname, email){Runs when a Person is created.
    this.surname = surname; this.name = name;Stores the names.
    if(this.validateEmail(email)) { this.email = email; }Checks the address format before storing it.
    else { throw new Error("Invalid email!"); }Rejects a bad address by throwing an error.
    validateEmail(email : string) {Email-format rule, living inside Person (the boxed part on the slide).
    var re = /^([\w-]+(?:\.[\w-]+)*)@(...)/;A regular expression describing a valid address.
    return re.test(email); }True if the address matches the pattern.
    greet() { alert("Hi!"); }Genuine person behavior.
  2. The boxed lines show the violation: Person now has two reasons to change. Its fields change when what a person is changes; validateEmail changes when the email rules change (new domains, longer TLDs). You also can’t test the email rule without building a Person.

Slide 18 / 39

SRP - example

Slide 18: SRP - example

Notes

  1. The refactoring moves validation into its own class:

    CodeWhat it does
    class Email {New class that owns everything about an email address.
    constructor(email){ if(this.validateEmail(email)) {...} else { throw ... } }An Email object can only exist if the address is valid.
    validateEmail(email){ ... return re.test(email); }The same rule, now in its only home.
    class Person {Back to pure person data and behavior.
    public email : Email;The field’s type is now Email, not string.
    constructor(name, surname, email : Email){ this.email = email; ... }Person receives an already-validated Email, so no check is needed.
    greet() { alert("Hi!"); }Unchanged.
  2. Each class now has one reason to change: email-rule changes touch only Email, and person changes touch only Person. A class with one responsibility (one actor) is also easier to read, test and extend.

Slide 19 / 39

SRP: does every function need its own class?

Slide 19: SRP: does every function need its own class?

Notes

  1. The violating Employee class stores data, calculates taxes and prints pay stubs. Three different things can force edits: the data structure, the tax laws and the print format.

  2. The SRP version keeps only the employee data in Employee. Tax calculation and pay-stub printing move to their own classes, so Employee changes only when the data changes.

  3. The slide’s question has a clear answer, given on slide 20: no. SRP does not mean one class per function.

Slide 20 / 39

SRP Applies to Responsibilities, Not Methods

Slide 20: SRP Applies to Responsibilities, Not Methods

Notes

  1. A responsibility is usually carried out by several related methods working together. SRP limits the number of responsibilities, not the number of methods.

  2. ReportGenerator mixes three responsibilities: calculation (calculateTax), formatting (printToPDF) and communication (sendEmail). Each changes for a different reason.

  3. TaxCalculator has three methods but one responsibility, tax calculation. The table adds two more good examples: UserAuthentication (login state, four methods) and ProductValidator (product data correctness, three methods).

  4. The test for splitting: if you change method A, would you also have to change method B? If yes, they belong together (both tax methods change with tax law). If no, they belong in separate classes (calculateTax and printToPDF).

Slide 21 / 39

In Class Exercises

Slide 21: In Class Exercises

Notes

  1. Three requirements means three potential reasons to change: pay rules, storage technology and report format.

  2. Question 1, a design that violates SRP (one class does everything):

    Employee
      - id, name, baseSalary
      + calculateSalary(): double
      + saveToDatabase(): void
      + generateReport(): String
  3. Question 2, the SRP redesign. Employee keeps the data; each job gets its own class, and each one depends on (uses) Employee:

    Employee
      - id, name, baseSalary          (data only)
    
    SalaryCalculator
      + calculateSalary(e: Employee): double
    EmployeeRepository
      + save(e: Employee): void
    EmployeeReportGenerator
      + generate(e: Employee): String
    
    all three  - - ->  Employee    (dependency)

Exam angle

Past exam · Final 252 · Q5-1 · 2.5 marksQ1

NextGen Library: a junior developer put fee calculation, book data and report formatting into one BookManager class. Which detailed-design principle is violated? Draw a class diagram that fixes it.

Show answer

The Single Responsibility Principle: BookManager has three reasons to change (book data, loan and fee rules, report format). Fix: Book (title, ISBN, author), LoanCalculator (due date and late fee) and MonthlyReportGenerator (text report), each with one responsibility; LoanCalculator and the report class use Book.

Slide 22 / 39

2- The Open-Closed Principle (OCP)

Slide 22: 2- The Open-Closed Principle (OCP)

Notes

  1. OCP is what lets a system keep growing for years without every new feature breaking old ones.

  2. “Open to extension” means you can add new behavior. “Closed for modification” means code that already works and is tested stays as it is.

  3. There is no contradiction: the system still changes, but by adding classes (a new subclass, a new implementation) that plug into existing abstractions. No working code has to be edited for that.

Exam angle

PredictionQ1

Explain how a design can be both “open for extension” and “closed for modification”.

Show answer

New behavior is added as new code (new classes implementing an existing interface or extending an abstract class) that the system uses through that abstraction, so existing, tested classes are never edited.

Slide 23 / 39

The Open-Close Principle

Slide 23: The Open-Close Principle

Notes

  1. The scenario matters: several kinds of characters exist, and more will be added. A change is expected, so the design must be ready for it.

  2. Reading the diagram:

    GameEngine
    Has one operation, add(pCharacter: TerrestrialCharacter*). The * is a C++ pointer.
    Dashed arrow
    A dependency: GameEngine uses TerrestrialCharacter directly.
    TerrestrialCharacter
    A concrete class with draw() and run().
  3. Why it violates OCP: GameEngine names a concrete class. Every new kind of character (one that flies, one that swims) forces a change to GameEngine, the code that already works.

Slide 24 / 39

The Open-Close Principle: the add() code

Slide 24: The Open-Close Principle: the add() code

Notes

  1. The C++ code, line by line:

    CodeWhat it does
    class TerrestrialCharacter {A character that moves over land.
    virtual void draw() { ... }virtual = subclasses may override it, and the call is resolved at run time.
    virtual void run() { ... }Land movement.
    class GameEngine {Manages the game.
    void add(TerrestrialCharacter* pCharacter) {Accepts only terrestrial characters.
    pCharacter->draw();-> calls a method through a pointer: show the character.
    pCharacter->run();Make it move by running.
  2. add(...) is hard-wired to one concrete type and one way of moving (run).

  3. An AerialCharacter that flies doesn’t fit: it isn’t a TerrestrialCharacter and has no run(). Supporting it means editing add(...), which is working code. That edit is the OCP violation.

Slide 25 / 39

The Open-Close Principle (OCP)

Slide 25: The Open-Close Principle (OCP)

Notes

  1. The fix students should propose: introduce an abstraction (an interface or abstract class) named Character with the operations every character has, and make GameEngine depend on Character instead of on a concrete class. Slides 26–29 show how a first attempt still goes wrong, and then the correct version.

Slide 26 / 39

The Open-Closed Principle (OCP): first attempt

Slide 26: The Open-Closed Principle (OCP): first attempt

Notes

  1. Joe’s first attempt (C++):

    CodeWhat it does
    class Character { virtual string getType() = 0; virtual void draw() = 0; };= 0 makes a method pure virtual (abstract). A class with only pure virtual methods acts as an interface.
    class AerialCharacter : public Character {: public Character = inherits from (realizes) Character.
    getType() { return "aerial"; } draw() {...} fly() {...}Reports its type, draws itself, and has its own fly().
    class TerrestrialCharacter : public Character {Same shape:
    getType() { return "terrestrial"; } draw() {...} run() {...}its own run().
  2. Joe abstracted the drawing, but movement was left to each subtype under different names (fly, run). Character itself has no movement operation.

  3. The getType() method is the warning sign: a caller that has to ask “what type are you?” is about to branch on the answer. Slide 27 shows that branch.

Slide 27 / 39

The Open-Closed Principle (OCP): GameEngine with type checks

Slide 27: The Open-Closed Principle (OCP): GameEngine with type checks

Notes

  1. GameEngine.add with Joe’s design:

    CodeWhat it does
    void add( Character* pCharacter ) {Now takes any Character, which is better.
    pCharacter->draw();Drawing works for every character.
    if( pCharacter->getType().compare("aerial") == 0 ) {Asks for the type string.
    AerialCharacter* pAerial = dynamic_cast<AerialCharacter*>(pCharacter);dynamic_cast = a checked downcast to the concrete type.
    pAerial->fly(); }Only then can it call fly().
    else { TerrestrialCharacter* pTerrestrial = dynamic_cast<...>(pCharacter);Otherwise assume terrestrial…
    pTerrestrial->run(); }…and run.
    test driver: TerrestrialCharacter madRabbit; AerialCharacter killerBee; engine.add(&madRabbit); engine.add(&killerBee);Prints: drawing terrestrial / running / drawing aerial / flying.
  2. Encapsulate variation: the part that changes (how each character moves) should be hidden behind one stable operation. Here it leaks into GameEngine as an if/else that “will always vary” with the set of characters.

  3. It runs correctly, but it still violates OCP: adding an AquaticCharacter means editing add() again with another branch and another cast.

Slide 28 / 39

The Open-Closed Principle (OCP): one move() for all

Slide 28: The Open-Closed Principle (OCP): one move() for all

Notes

  1. The fix: put one movement operation, move(), into Character itself (virtual void move() = 0). “How it moves” becomes each subtype’s private business.

  2. Each subtype implements the full contract:

    AerialCharacter
    draw() draws it, move() makes it fly.
    TerrestialCharacter (sic)
    draw() draws it, move() makes it run.
Slide 29 / 39

The Open-Closed Principle (OCP): the redesign

Slide 29: The Open-Closed Principle (OCP): the redesign

Notes

  1. Reading the new diagram (left) against the old one (right):

    GameEngine - - -> «interface» Character
    GameEngine depends only on the interface. The italic +draw() and +move() are abstract operations.
    Aerial / Terrestrial / AquaticCharacter
    Each realizes Character (dashed line, hollow triangle) and implements both operations.
    Old design
    GameEngine depended on the concrete TerrestrialCharacter.
  2. AquaticCharacter is added by writing one new class. GameEngine, Character and the existing characters are not touched. That is OCP.

  3. add() now just calls draw() and move(); polymorphism (slide 14) runs the right movement. The same design in Java:

    interface Character {
        void draw();
        void move();
    }
    class AquaticCharacter implements Character {
        public void draw() { /* draw a fish */ }
        public void move() { /* swim */ }
    }
    class GameEngine {          // never changes
        void add(Character c) {
            c.draw();
            c.move();
        }
    }
Slide 30 / 39

The Open-Closed Principle (OCP)

Slide 30: The Open-Closed Principle (OCP)

Notes

  1. OCP is a direction, not an absolute. Something always has to be edited: a factory, a configuration or a list of characters.

  2. The skill is to predict where change is likely (new character types, new salary types, new payment methods), hide that variation behind an abstraction, and let polymorphism pick the implementation.

Slide 31 / 39

Exercise

Slide 31: Exercise

Notes

  1. The same rule restated for any software entity: a class, module or function.

  2. Model answer: make the varying part (how a salary is computed) an interface, so a new salary type is a new class.

    «interface» SalaryPolicy
      + calculate(e: Employee): double
    
    MonthlySalary, HourlySalary, WeeklySalary
      - - -|>  SalaryPolicy        (realization)
    
    SalaryCalculator               (never edited)
      + calculateSalary(e: Employee,
                        p: SalaryPolicy): double
    
    New CommissionSalary = one new class
    that implements SalaryPolicy.

Exam angle

Past exam · Final 252 · Q5-2 · 2.5 marksQ1

NextGen Library (physical books now, E-books soon): by applying the Open/Closed Principle for E-books, draw the class diagram.

Show answer

Introduce an abstract class or interface (e.g. LibraryItem with title, ISBN, author and an abstract operation such as calculateLateFee()). PhysicalBook and EBook extend or implement it. The loan and report classes depend only on LibraryItem, so adding EBook (or audiobooks later) needs no change to existing classes.

Slide 32 / 39

3- The Liskov Substitution Principle (LSP)

Slide 32: 3- The Liskov Substitution Principle (LSP)

Notes

  1. The “implied contract” is everything callers rely on: the method names and types, but also the behavior (what the method promises to do and what it requires before it is called).

  2. If code works with a parent object, it must keep working when you hand it a child object instead. The slide calls this the “treat alike” rule.

  3. Two requirements: the method signatures stay compatible, and anything you could conclude from the parent’s specification must still hold for the subtype.

Exam angle

PredictionQ1

State the Liskov Substitution Principle in one sentence.

Show answer

Objects of a superclass must be replaceable by objects of any subclass without altering the correctness of the program: subtypes must honor the base type’s contract.

Slide 33 / 39

The Liskov Substitution Principle (LSP)

Slide 33: The Liskov Substitution Principle (LSP)

Notes

  1. Signature rule: if a program type-checks against the parent’s methods, it type-checks with the child too (same methods with compatible parameters and return types). The compiler mostly enforces this.

  2. Methods rule: reasoning about a call stays valid even though the child’s code runs. If move() promises “moves over land”, every override must still move over land. The compiler can’t check this, so the designer must.

  3. The plain-language version: a child class must work anywhere the parent works, without anything breaking.

Exam angle

PredictionQ1

Which LSP rule does the compiler NOT check for you, and why?

Show answer

The methods rule (behavior). It concerns what methods do: pre- and postconditions and side effects. Types alone can’t express that, so a subclass can compile and still break callers.

Slide 34 / 39

The Liskov Substitution Principle (LSP) - example

Slide 34: The Liskov Substitution Principle (LSP) - example

Notes

  1. The code and its contract:

    CodeWhat it does
    class TerrestrialCharacter : public Character {The parent.
    // Pre-Condition: Character is drawn on screen.What must be true before calling move().
    // Post-Condition: Character roams around randomly over land...What move() promises: it stays on land, walking or running.
    virtual void move() { //code to make the character walk or run }Honors the promise.
    class EntryLevelTerrestrialCharacter : public TerrestrialCharacter {A “kind of” terrestrial character.
    virtual void move() { // code to make the character fly. }Overrides move() to FLY.
  2. Answer: no, it violates LSP. It compiles (signature rule OK) but breaks the postcondition “roams over land”. Code that relies on terrestrial characters staying on the ground, such as collision with water or bridges, misbehaves when given this subclass.

  3. The lesson in red: OCP alone isn’t enough. The class was added by extension (OCP satisfied), yet the design is wrong because the subtype isn’t substitutable. A similar trap is a subclass whose move() throws an exception (“Ghosts don’t move!”): that also breaks the contract.

Exam angle

PredictionQ1

A subclass compiles and overrides every method, but one override throws UnsupportedOperationException. Does it satisfy LSP?

Show answer

No. It satisfies the signature rule but violates the methods rule: callers of the base type expect the operation to work. The subclass can’t replace its parent without breaking the program.

Slide 35 / 39

The Liskov Substitution Principle (LSP) – example 2

Slide 35: The Liskov Substitution Principle (LSP) – example 2

Notes

  1. The classic LSP violation. Mathematically every square is a rectangle, but in software Square should not inherit from Rectangle.

  2. Rectangle’s implied contract: setH and setW change only their own dimension. A user that sets width 5 and height 2 expects an area of 10.

  3. A Square has to keep its sides equal, so its setters change both. After setW(5); setH(2) the area is 4, not 10.

  4. Diagram and fix:

    User → Rectangle
    Solid arrow: User is associated with and uses Rectangle (+setH, +setW).
    Square → Rectangle (hollow triangle)
    Generalization: Square inherits from Rectangle and adds +setSide.
    Fix
    Don’t inherit. Make Square and Rectangle siblings under a Shape abstraction that offers only what both can honor, e.g. area().

Exam angle

PredictionQ1

Why does Square extends Rectangle violate LSP? Use a short example.

Show answer

Rectangle lets width and height change independently. Code like r.setW(5); r.setH(2); assert r.area()==10 works for a Rectangle, but a Square forces both sides equal, so the area is 4 and the assertion fails. A Square can’t substitute for a Rectangle.

Slide 36 / 39

The Liskov Substitution Principle (LSP) – example 3

Slide 36: The Liskov Substitution Principle (LSP) – example 3

Notes

  1. Reading the diagram:

    Billing → License
    Billing uses License and calls calcFee().
    <I> License
    An interface with one operation, + calcFee().
    PersonalLicense, BusinessLicense
    Implement License. BusinessLicense also keeps - users (a private field) because its fee depends on the number of users.
  2. Each subtype computes the fee differently (a flat personal fee; a fee per business user), but both honor the contract “return this license’s fee”.

  3. Billing’s behavior never depends on which subtype it receives, so both are substitutable: LSP holds. It is also OCP-compliant, because a new license type is a new class. In Java:

    interface License { double calcFee(); }
    
    class PersonalLicense implements License {
        public double calcFee() { return 100.0; }
    }
    class BusinessLicense implements License {
        private int users;
        public double calcFee() {
            return users * 50.0;
        }
    }
    class Billing {
        void printBill(License l) {
            System.out.println(l.calcFee());
        }
    }
Slide 37 / 39

LSP: a sanity check

Slide 37: LSP: a sanity check

Notes

  1. Use LSP to sanity-check every inheritance arrow you draw: ask whether the child can stand in for the parent everywhere.

  2. Inheritance is about keeping expected behavior, not just reusing code. If the honest answer is “only sometimes”, use composition or a different abstraction instead.

Slide 38 / 39

Exercise

Slide 38: Exercise

Notes

  1. Fruit promises two operations every fruit can do: Peel() and MakeSmoothie().

  2. Orange: peeling and blending both work exactly as Fruit promises. LSP holds.

  3. Coconut: you don’t peel a coconut like other fruit; you crack it open. If Coconut.Peel() throws an error or does something callers don’t expect, a program that peels any Fruit breaks. LSP is violated. Model drawing and fix:

    Violation
      Fruit  (+Peel(), +MakeSmoothie())
        ^ Orange    both work
        ^ Coconut   Peel() fails
    
    Fix
      «interface» Peelable   (+Peel())
      «interface» Blendable  (+MakeSmoothie())
      Orange  implements Peelable, Blendable
      Coconut implements Blendable
              and adds its own +Crack()

Exam angle

PredictionQ1

Does Coconut violate LSP if Fruit declares Peel()? How would you fix the design?

Show answer

Yes, if Coconut can’t honor Peel() (it throws or behaves differently), code written for Fruit breaks. Fix: don’t promise Peel() for every fruit. Split the capabilities into separate interfaces (Peelable, Blendable) and have each fruit implement only what it can do. This also previews the Interface Segregation Principle.

Slide 39 / 39

Lecture Outlines

Slide 39: Lecture Outlines

Notes

  1. Covered so far: component design, the OO refresher, and S, O and L. The Interface Segregation and Dependency Inversion principles come in the next lecture.

  2. Also next lecture: designing the internal behavior of components.