SE322 · Chapter 5 · Lecture 3 · Annotated SlidesPrinciples of Detailed Design III: ISP, DIP, Internal Behaviour and Rules of Design
Chapter 5 Quiz PDF
Overview

Principles of Detailed Design III: ISP, DIP, Internal Behaviour and Rules of Design

This lecture finishes SOLID. The Interface Segregation Principle says no class should be forced to depend on methods it does not use, so one bloated interface is split into small, role-specific ones. The Dependency Inversion Principle says high-level modules and low-level details should both depend on abstractions, so a Manager talks to an IWorker interface instead of a concrete Worker. It then shows how a component’s internal behaviour is modelled (sequence diagrams, next to class diagrams for structure), and closes with sixteen rules of design and four common design mistakes.

ISP

Many client-specific interfaces are better than one general-purpose interface. Classes shouldn’t implement methods they can’t honour.

DIP

Depend on abstractions, not concretions: high-level policy and low-level detail both point at an interface.

Structure vs. behaviour

A component’s internal structure is a class diagram; its internal behaviour is a sequence diagram.

Rules of design

What before how, external before internal, keep it simple, depend on abstractions, dependencies toward stability.

How the lecture is organized

  • Slides 2–8Interface Segregation Principle: the enemy-character game and the Worker example
  • Slides 9–14Dependency Inversion Principle: charger, switch and Manager examples
  • Slide 15Modelling the internal behaviour of a component
  • Slides 16–23Rules of design and common design mistakes
  • Slide 24From analysis models to design models

Exam focus

SRP and OCP from Lecture 2 have appeared on Final 252; ISP and DIP have not yet, so the questions here are predictions. Expect “which principle is violated?” scenarios: a class forced to implement a method it can’t support (ISP), or a high-level class that holds a concrete low-level class (DIP).

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 24 slides
  1. 1 Slide 1
  2. 2 Lecture Outlines
  3. 3 Interface Segregation Principle (ISP)
  4. 4 ISP: one general interface vs. many specific ones
  5. 5 Extra: ISP and LSP explained
  6. 6 ISP: the segregated design
  7. 7 ISP: the segregated design in Java
  8. 8 ISP: Worker violation vs. applied
  9. 9 Dependency Inversion Principle (DIP)
  10. 10 Dependency Inversion Principle (DIP)
  11. 11 Extra: DIP – charger and switch (bad)
  12. 12 Extra: DIP – switch with an interface (good)
  13. 13 Dependency Inversion
  14. 14 Dependency Inversion Principle (DIP)
  15. 15 Modeling Internal Behavior of
  16. 16 Rules of Design
  17. 17 Rules of Design
  18. 18 Rules of Design
  19. 19 Rules of Design
  20. 20 Rules of Design
  21. 21 Common Design Mistakes
  22. 22 Summary
  23. 23 Extra: The rules of design in one list
  24. 24 Analysis Models to Design Models
Slide 1 / 24

Slide 1

Slide 1: Slide 1

Notes

  1. Third detailed-design lecture: the last two SOLID principles (I and D), then how to model a component’s behaviour and general rules of good design.

Slide 2 / 24

Lecture Outlines

Slide 2: Lecture Outlines

Notes

  1. Already covered in Lecture 2: SRP, OCP and LSP.

  2. This lecture: the Interface Segregation Principle and the Dependency Inversion Principle, completing SOLID.

  3. Then designing internal behaviour and the rules of design.

Slide 3 / 24

Interface Segregation Principle (ISP)

Slide 3: Interface Segregation Principle (ISP)

Notes

  1. The starting point is SRP: a well-designed class has one reason to change.

  2. When that is violated, the interfaces the class offers usually provide more than they should, which makes designs harder to maintain and reuse.

  3. The Interface Segregation Principle (ISP): classes (or methods) should not be forced to depend on methods they do not use.

  4. The example: a game with enemy characters that roam over land, fly or swim, plus a super enemy that can do all three.

  5. The tempting design: one EnemyCharacter with fly(), run() and swim(), inherited by Aerial, Terrestrial, Aquatic and SuperCharacter. The slide labels it a bloated interface.

Exam angle

PredictionQ1

“Clients should not be forced to depend on methods they do not use” states the:

  1. Interface Segregation Principle
  2. Dependency Inversion Principle
  3. Open-Closed Principle
  4. Liskov Substitution Principle
Show answer

A. Interface Segregation Principle

Slide 4 / 24

ISP: one general interface vs. many specific ones

Slide 4: ISP: one general interface vs. many specific ones

Notes

  1. The rule of thumb: many client-specific interfaces are better than one general-purpose interface.

  2. Aerial, Terrestrial and Aquatic characters are polluted with methods they can’t perform: an aerial character inherits run() and swim(), a terrestrial one fly().

  3. The design also violates LSP (Lecture 2): code holding an EnemyCharacter may call fly(), but a TerrestrialCharacter put in its place can’t fly, so it can’t safely substitute for its parent.

  4. Only SuperCharacter conforms to the base type syntactically and semantically. It is the only class that really needs (and honours) all three methods, so it is the only one that satisfies LSP.

Exam angle

PredictionQ1

In the enemy-character design where every character extends EnemyCharacter with fly(), run() and swim(), which principles are violated and why?

Show answer

ISP: Aerial, Terrestrial and Aquatic characters are forced to depend on methods they don’t use. LSP: a TerrestrialCharacter cannot replace an EnemyCharacter where code expects fly(), so subclasses don’t behave like their parent. Only SuperCharacter conforms.

Slide 5 / 24

Extra: ISP and LSP explained

Slide 5: Extra: ISP and LSP explained

Notes

  1. ISP: use small, specific interfaces instead of one big interface full of unused methods. Problem here: AerialCharacter and AquaticCharacter are forced to implement run(); the interface is polluted.

  2. LSP: a subclass must replace its parent without breaking correctness. Problem here: a TerrestrialCharacter can’t replace an EnemyCharacter when the code expects fly().

  3. In short: ISP → interfaces should be specific; LSP → subclasses must behave consistently with their base class. Fixing the interfaces (next slide) fixes both.

Slide 6 / 24

ISP: the segregated design

Slide 6: ISP: the segregated design

Notes

  1. The ISP fix: three small interfaces, «interface» AerialCharacter (fly()), TerrestrialCharacter (run()) and AquaticCharacter (swim()). Each client class (dashed realization arrows) implements only the one it can honour.

  2. «interface» SuperCharacter declares all three operations and is implemented by SuperCharacterClient. In the slide’s version the three small interfaces extend SuperCharacter (hollow triangles pointing down to it); conceptually it is the combination of the three capabilities.

  3. The Extra box: Java rules about overriding when types are combined:

    Abstract class implements an interface
    No override needed: it can leave the methods abstract.
    Abstract class extended by a concrete class
    Yes: the concrete subclass must override every abstract method.
    Normal class implements an interface
    It must override all the interface’s abstract methods.
    Interface extends interface
    No override: interfaces only add declarations.
Slide 7 / 24

ISP: the segregated design in Java

Slide 7: ISP: the segregated design in Java

Notes

  1. The code, top to bottom:

    CodeWhat it does
    interface SuperCharacter { void fly(); void run(); void swim(); }The full contract: all three capabilities.
    interface AerialCharacter extends SuperCharacter { void fly(); }A specialised interface that names the capability its clients use. (Because it extends SuperCharacter, a class implementing it must still provide all three methods; to apply ISP strictly, the small interfaces should not extend the big one.)
    interface TerrestrialCharacter extends SuperCharacter { void run(); }The same for running.
    interface AquaticCharacter extends SuperCharacter { void swim(); }The same for swimming.
    class SuperCharacterClient implements SuperCharacter { fly(){…} run(){…} swim(){…} }The only class that really supports all three; each prints what it is doing.
    class AerialCharacterClient implements AerialCharacter { public void fly(){ … "is flying" } }Implements only fly(). As printed it would not compile, because the inherited run() and swim() are missing; with stand-alone small interfaces it would.
    class TerrestrialCharacterClient implements TerrestrialCharacter { run() }Runs only.
    class AquaticCharacterClient implements AquaticCharacter { swim() }Swims only.
    AerialCharacterClient arialObject = new AerialCharacterClient(); arialObject.fly();In main, each object is used through the capability it supports.
    SuperCharacterClient superCharacter = new SuperCharacterClient(); superCharacter.fly(); …swim(); …run();The super character can be asked for all three.
  2. The intent: a reference of a narrow type can only request what that character can do, so unsupported calls become impossible. Exam answers should draw the small interfaces as independent, each implemented by the classes that need it.

Slide 8 / 24

ISP: Worker violation vs. applied

Slide 8: ISP: Worker violation vs. applied

Notes

  1. Violation: one Worker interface with work() and eat().

    CodeWhat it does
    interface Worker { void work(); void eat(); }One fat interface for two unrelated capabilities.
    class Robot implements Worker {A robot can work but cannot eat…
    public void work() { System.out.println("Robot working"); }…so work() is meaningful,
    public void eat() { } // Robot does not eatbut eat() is forced on it and left empty: a lie in the type.
  2. Applied: split the interface by capability.

    CodeWhat it does
    interface Workable { void work(); }The working capability.
    interface Eatable { void eat(); }The eating capability.
    class Robot implements Workable { work() }The robot implements only what it can do.
    class Human implements Workable, Eatable { work() … eat() }A human implements both: a class may implement several small interfaces.

Exam angle

PredictionQ1

A Robot class implements a Worker interface that declares work() and eat(), and leaves eat() empty. Which principle is violated, and how do you fix it?

Show answer

The Interface Segregation Principle: Robot is forced to depend on eat(), which it does not use. Split Worker into Workable (work()) and Eatable (eat()); Robot implements Workable only, Human implements both.

Slide 9 / 24

Dependency Inversion Principle (DIP)

Slide 9: Dependency Inversion Principle (DIP)

Notes

  1. The Dependency Inversion Principle (DIP): the most flexible systems are those whose source-code dependencies refer only to abstractions, not to concretions.

  2. In a statically typed language like Java, that means use, import and include statements should name only interfaces, abstract classes or other abstract declarations.

  3. Nothing concrete should be depended on: concrete classes change often, abstractions rarely.

  4. In particular, avoid depending on modules that are actively being developed and change frequently: every change there would ripple into your code.

Slide 10 / 24

Dependency Inversion Principle (DIP)

Slide 10: Dependency Inversion Principle (DIP)

Notes

  1. The two rules. (1) High-level modules (policy, e.g. a Manager) must not depend on low-level modules (details, e.g. a Worker). Both depend on an abstraction (an interface).

  2. (2) Abstractions must not depend on details; details depend on abstractions.

  3. The diagram: without inversion, Object A in Package A references Object B in Package B directly. With inversion, Object A references Interface A, and Object B implements (inherits) Interface A. The arrow from B now points toward the abstraction, the opposite (“inverted”) direction of the original dependency.

Exam angle

PredictionQ1

According to DIP, a high-level module should depend on:

  1. Abstractions (interfaces)
  2. Low-level modules
  3. Concrete classes
  4. Global variables
Show answer

A. Abstractions (interfaces)

Slide 11 / 24

Extra: DIP – charger and switch (bad)

Slide 11: Extra: DIP – charger and switch (bad)

Notes

  1. The real-life idea: a charger that only works with Samsung phones is useless when you change phones (bad). A USB charger works with any phone (good): USB is the interface, the abstraction.

  2. The same mistake in code, the charger that only fits one phone:

  3. Switch without dependency inversion:

    CodeWhat it does
    class Fan { void turnOn() { System.out.println("Fan is ON"); } }A concrete low-level device.
    class Switch {The high-level module.
    Fan fan = new Fan(); // directly depends on FanThe switch creates and holds a concrete Fan.
    void press() { fan.turnOn(); } }Pressing can only ever turn on a fan.
  4. The problem: Switch works only with Fan. To control a Light you must change Switch.

Slide 12 / 24

Extra: DIP – switch with an interface (good)

Slide 12: Extra: DIP – switch with an interface (good)

Notes

  1. The fix in three steps:

    CodeWhat it does
    interface Device { void turnOn(); }Step 1: the abstraction (the “USB port”).
    class Fan implements Device { public void turnOn() { …"Fan is ON"… } }Step 2: the details implement the abstraction.
    class Light implements Device { public void turnOn() { …"Light is ON"… } }A second device, added without touching Switch.
    class Switch { Device device; Switch(Device device) { this.device = device; } void press() { device.turnOn(); } }Step 3: Switch knows only Device; the concrete device is passed in through the constructor.
    Switch s1 = new Switch(new Fan()); s1.press();Wiring a fan: prints “Fan is ON”.
    Switch s2 = new Switch(new Light()); s2.press();Wiring a light: prints “Light is ON”. Same Switch class.
  2. Passing the dependency in from outside (instead of new Fan() inside) is called dependency injection; it is the usual way to apply DIP.

Slide 13 / 24

Dependency Inversion

Slide 13: Dependency Inversion

Notes

  1. The textbook bad example, line by line:

    CodeWhat it does
    class Worker { public void work() { // ....working } }A concrete worker.
    class Manager {The high-level module (the boss).
    Worker worker;The manager is fixed to one concrete type.
    public void setWorker(Worker w) { worker = w; }It can only be given a Worker (the circled parameter type).
    public void manage() { worker.work(); } }It tells that worker to work.
  2. Manager is the high-level module and depends directly on the low-level Worker.

  3. Problem: the boss only understands Worker. A Robot or an Intern can’t be managed without changing Manager, and tests can’t substitute a fake worker.

Slide 14 / 24

Dependency Inversion Principle (DIP)

Slide 14: Dependency Inversion Principle (DIP)

Notes

  1. The good example:

    CodeWhat it does
    interface IWorker { public void work(); }The contract: any worker must know how to work.
    class Worker implements IWorker { public void work() { // ....working } }The original worker now follows the contract.
    class RobotWorker implements IWorker { public void work() { …"Robot is working..."… } }A new kind of worker, added without touching Manager.
  2. Manager now depends only on the abstraction:

    CodeWhat it does
    class Manager { IWorker worker;The boss only cares about the interface, not the exact type.
    public void setWorker(IWorker w) { worker = w; }Any worker (human, robot, AI, intern) can be assigned.
    public void manage() { worker.work(); } }At run time the actual object’s work() runs.
  3. Bad code: Manager → Worker (tightly coupled). Good code: Manager → IWorker ← Worker, RobotWorker (loosely coupled and flexible). New workers such as RobotWorker, AIWorker or InternWorker need no change to Manager, which also makes Manager open for extension (OCP).

Exam angle

PredictionQ1

A Manager class holds a field of the concrete type Worker and calls worker.work(). Which principle does this violate? Draw or describe the fix.

Show answer

The Dependency Inversion Principle: the high-level Manager depends on the concrete low-level Worker. Fix: introduce interface IWorker { work(); }; Worker, RobotWorker, etc. implement it; Manager holds an IWorker and receives it through setWorker(IWorker). In UML: Manager → IWorker (dependency/association), Worker and RobotWorker ⇢ IWorker (realization).

Slide 15 / 24

Modeling Internal Behavior of

Slide 15: Modeling Internal Behavior of

Notes

  1. Component design models both the internal structure and the behaviour of a component.

  2. The internal structure is modelled with a class diagram (as in Lecture 2).

  3. The internal behaviour is modelled with a sequence diagram.

  4. Reading the “Activate Sensors” example:

    1: timer expires
    An outside event reaches the ScheduleManager (sm).
    2: getMsg() / 4: interpretMsg()
    It fetches the scheduled message from s : Schedule and interprets it (a self-call).
    alt frame
    A choice: if msgId == HEAT_MSG_ID it sends 5: activate() to the HeatSensor; if msgId == VIBRATE_MSG_ID it sends 6: activate() to the VibrationSensor. Only one branch runs.

Exam angle

PredictionQ1

Which UML diagrams model a component’s internal structure and its internal behaviour?

  1. Class diagram for structure, sequence diagram for behaviour
  2. Deployment diagram for structure, use case diagram for behaviour
  3. Package diagram for both
  4. Sequence diagram for structure, class diagram for behaviour
Show answer

A. Class diagram for structure, sequence diagram for behaviour

Slide 16 / 24

Rules of Design

Slide 16: Rules of Design

Notes

  1. Make sure the problem is well defined: know all design criteria, requirements and constraints before starting the design.

  2. What comes before how: at every level of abstraction, define the functions to be performed before choosing the structures that implement them.

  3. Separate orthogonal concerns: don’t connect what is independent (orthogonal = unrelated, changing for different reasons).

Slide 17 / 24

Rules of Design

Slide 17: Rules of Design

Notes

  1. External before internal: first treat the solution as a black box and decide how it interacts with its environment; then decide how the box is organised inside, usually as smaller black boxes refined the same way.

  2. Keep it simple: fancy designs are buggier, harder to implement and verify, and often slower. Problems that look complex are often simple problems huddled together; the designer’s job is to separate them and solve each one.

Slide 18 / 24

Rules of Design

Slide 18: Rules of Design

Notes

  1. Work at multiple levels of abstraction: good designers move quickly between the big picture and the details.

  2. Design for extensibility: a good design is “open-ended”, easy to extend. Don’t introduce what is immaterial (unimportant) and don’t restrict what is irrelevant (unrelated): both make later extension harder.

Slide 19 / 24

Rules of Design

Slide 19: Rules of Design

Notes

  1. Rapid prototyping: before implementing, build a high-level prototype and check that the design criteria are met.

  2. Details depend on abstractions; abstractions don’t depend on details: the DIP stated as a rule of design.

  3. Common closure: classes in a released component should change together; if one needs to change, they all probably do (“what affects one, affects all”). Group classes by the reasons they change.

Slide 20 / 24

Rules of Design

Slide 20: Rules of Design

Notes

  1. Common reuse: classes in a released component are reused together; you can’t take part of a component.

  2. Stable dependencies: dependencies between components must run in the direction of stability. The component depended on (dependee) must be more stable than the one depending on it.

  3. Stable abstractions: the more stable a component is, the more it should consist of abstract classes; a completely stable component contains nothing but abstractions. That is why interfaces sit at the stable end of DIP diagrams.

Exam angle

PredictionQ1

State the stable-dependencies rule and explain why stable components should be abstract.

Show answer

Dependencies must point toward more stable components (the dependee is more stable than the depender). Stable components are hard to change because many others depend on them, so they should consist of abstractions that can be extended without modification.

Slide 21 / 24

Common Design Mistakes

Slide 21: Common Design Mistakes

Notes

  1. Depth-first design: perfecting one part early and only partially satisfying the requirements overall. Experience is the best cure.

  2. Directly refining the requirements specification into a design: produces overly constrained, inefficient designs, because the structure copies the document instead of the problem.

  3. Failing to consider potential changes: always design for extension and contraction (adding and removing features).

  4. Making the design too detailed: over-constrains the implementation and leaves programmers no sensible choices.

Exam angle

PredictionQ1

Name two common design mistakes and their consequences.

Show answer

Any two: depth-first design (only partially satisfies requirements); directly refining the requirements specification (overly constrained, inefficient design); failing to consider potential changes (hard to extend); making the design too detailed (over-constrains the implementation).

Slide 22 / 24

Summary

Slide 22: Summary

Notes

  1. Recap: the internal-component design principles (the slide lists SRP and DIP; this lecture added ISP and DIP to Lecture 2’s SRP, OCP and LSP).

  2. Internal behaviour is modelled with sequence diagrams, and the rules of design guide every decision.

  3. Next chapter: creational design patterns.

Slide 23 / 24

Extra: The rules of design in one list

Slide 23: Extra: The rules of design in one list

Notes

  1. The rules from slides 16–20 in one list. Problem and requirements first; what before how; separate independent concerns; design external behaviour first.

  2. Keep it simple; work at several abstraction levels; design for extensibility; avoid the unnecessary; prototype early.

  3. Depend on abstractions, not details (DIP).

  4. Component rules: classes that change together and are reused together belong in one component; dependencies go toward stability; stable components should be abstract.

Slide 24 / 24

Analysis Models to Design Models

Slide 24: Analysis Models to Design Models

Notes

  1. How analysis (requirements) models turn into design models. Left: the analysis models describe the problem.

  2. The domain model class diagram (Customer, Order) becomes the design class diagrams (with attributes and operations such as changeName(), shipOrder()); use case diagrams and activity diagrams feed the component and interaction (sequence) diagrams.

  3. System sequence diagrams become detailed interaction diagrams; requirements state machines (Ready → Shipped) become design state machines; and package diagrams organise the result into layers. Each design model traces back to an analysis model.