Chapter 5: UML Review, Abstract Classes, Interfaces

0.0(0)
Studied by 1 person
call kaiCall Kai
learnLearn
examPractice Test
spaced repetitionSpaced Repetition
heart puzzleMatch
flashcardsFlashcards
GameKnowt Play
Card Sorting

1/98

encourage image

There's no tags or description

Looks like no tags are added yet.

Last updated 6:57 PM on 10/15/26
Name
Mastery
Learn
Test
Matching
Spaced
Call with Kai
Chat

No analytics yet

Send a link to your students to track their progress

99 Terms

1
New cards

What is this function declaring?

virtual void eat() = 0;

This is a virtual function. It declares that the function exists, but has no implementation.

It states that each class must provide its own implementation of the eat() function.

2
New cards

What is an abstract class?

An abstract class is a class with at least one virtual function. It can also contain data and the implementations of other functions.

3
New cards

Is this an example of an abstract class?

class Animal {
  protected:
    int age;

  public:
    :age(0)
    {
    }

  void sleep() {
    std::cout << "Sleeping\n" << std::endl;
  }

  virtual void eat() = 0;

};


Yes. The only thing making it abstract is virtual void eat() = 0;

4
New cards

What is an interface?

An interface is a class that only contains pure virtual functions (no functions with any kind of implementation, and no data). It must also contain a virtual destructor.

5
New cards

Is this an interface?

class Animal {
  public:
    virtual ~Animal() {}

    virtual void move_x(int x) = 0;
    virtual void move_y(int y) = 0;
    virtual void eat() = 0;
};


Yes, it is an interface. It does not have any kind of data like int age, or std::string, and it only contains pure virtual functions, plus a virtual destructor.

6
New cards

How do we know when to add a virtual destructor?

A virtual destructor should only be added if the class is intended to be a base class, an interface, or an abstract class.

7
New cards

What kind of class is this?

class Animal {
  public:
    virtual void eat() = 0;
};


This is an abstract class.

But technically, it is also acting like an interface. The only reason we wouldn’t call it an interface yet is because it doesn’t contain a virtual destructor.

8
New cards

Given this class:

class Animal {
  public:
    virtual void eat() = 0;
};

What will happen if we then try doing:

Animal animal;


We will get a compiler error.

This is because eat() doesn’t actually exist yet, and the compiler only knows that every child must implement eat(). If it doesn’t, then C++ will not allow you to create an object from it.

9
New cards

Why would we create an interface?

To force subclasses to implement certain behavior.

10
New cards

Let’s say we have the following Animal interface:

class Animal {
  public:
    virtual ~Animal() {}

    virtual void move_x(int x) = 0;
    virtual void move_y(int y) = 0;
    virtual void eat() = 0;
};

And then we create a Dog class that extends Animal, but we forgot to implement eat!

What will happen?

class Dog : public Animal {
  public:
    void move_x(int x) override {
      std::cout << "Dog Moved in x direction";
    }

    void move_y(int y) override {
      std::cout << "Dog Moved in y direction";
    }
};


The compiler will not allow us to forget any of these functions. If we leave one out, then Dog remains abstract, and we can never initialize any objects from it.

11
New cards

What’s the difference between an abstract class and an interface?

An abstract class may contain data, implemented functions, and pure virtual functions.

On the other hand, an interface may only contain pure virtual functions and a virtual destructor.

12
New cards
  1. Create an interface called Vehicle that requires start() and stop(). Then create a Car class that implements both functions. In main(), create a Car object and call both functions.


class Vehicle {
    public:
    virtual void start() = 0;
    virtual void stop() = 0;
    virtual ~Vehicle() {};
};

class Car : public Vehicle {
    public:
    void start() override {
        std::cout << "Car starting!\n";
    }

    void stop() override {
        std::cout << "Car stopping\n";
    }
};

int main() {
    Car myCar = Car();
    myCar.start();
    myCar.stop();
    return 0;
}


13
New cards

Create an abstract class called: Employee that contains: std::string name; and a constructor that initializes the name. Also require:

virtual void work() = 0;

Create a child class:

Programmer, that implements work().

Print the programmer's name and work message.

#include <string>
#include <iostream>

class Employee {
    public:
    std::string name;

    Employee(std::string n)
      :name(n)
      {
      }

      virtual void work() = 0;
};

class Programmer : public Employee {
    public:
    Programmer(std::string n)
        :Employee(n)
        {
        }

    void work() override {
        std::cout << "Hello I am " << name << "." << "I am working on code.";
    }
};

int main() {
    Programmer frank("Frank");
    Programmer sofia = Programmer("Sofia");
    sofia.work();
    frank.work();
    return 0;
}


14
New cards
  1. Create an interface called: Shape requiring: draw() and resize().

  • Then create: Circle that implements both functions.

  • Make each function print a message. Then test everything inside main()


#include <iostream>
#include <string>

class Shape {
    public:
    virtual ~Shape() {}
    virtual void draw() = 0;
    virtual void resize() = 0;
};

class Circle : public Shape {
    public:
    void draw() override {
        std::cout << "Drawing a circle" << std::endl;
    }

    void resize() override {
        std::cout << "Resizing a circle" << std::endl;
    }
};

int main() {
    Circle myCircle;
    myCircle.draw();
    myCircle.resize();
    return 0;
}

Note: The only reason we didn’t have to create a Circle constructor is because Circle has no data members. As a result, it relies on the default constructor, and we don’t need to define our own.

15
New cards

Identify the problems with the following code:

#include <string>
#include <iostream>

class Employee {
    public:
    std::string name;

    Employee(std::string n)
      :name(n)
      {
      }

      virtual void work() = 0;
};

class Programmer : public Employee {
    public:
    void work() override {
        std::cout << "Hello I am " << name << "." << "I am working on code." << std::endl;
    }
};

int main() {
    Programmer sofia = Employee("Sofia");
    Programmer frank = Programmer("Frank");
    sofia.work();
    frank.work();
    return 0;
}


#include <string>
#include <iostream>

class Employee {
    public:
    std::string name;

    Employee(std::string n)
      :name(n)
      {
      }

      virtual void work() = 0;
};

class Programmer : public Employee {
    public:

// PROBLEM #1. MISSING A CONSTRUCTOR IN THE PROGRAMMER CLASS
// WITHOUT THE FOLLOWING LINE, IT
// WILL CALL THE EMPLOYEE CONSTRUCTOR:
    Programmer(std::string n)
        :Employee(n) 
        {
        } 
 

    void work() override {
        std::cout << "Hello I am " << name << "." << "I am working on code." << std::endl;
    }
};

int main() {
// PROBLEM #2. THIS LINE IS ILLEGAL. 
// BECAUSE WE CANNOT INSTANTIATE AN ABSTRACT CLASS.
    // Programmer sofia = Employee("Sofia");

// This becomes okay once we add a constructor in Programmer.
    Programmer frank = Programmer("Frank");
// However, a simpler way to do it is just:
// Programmer frank("Frank");
    sofia.work();
    frank.work();
    return 0;
}


16
New cards

Can we instantiate objects of Abstract classes or Interfaces?

No, we cannot.

17
New cards

Create a table that shows the similarities and differences between an Interface, an Abstract class, and a Concrete class.

An Interface

  • Can only contain purely virtual functions

  • Must contain a virtual destructor

  • Has no data members

  • Cannot be instantiated

An Abstract Class

  • Must contain at least one virtual function

  • Must contain a virtual destructor

  • Can have functions and data members

  • Cannot be instantiated

A Concrete Class

  • Has no virtual functions

  • Must contain a virtual destructor if it is a base class

  • Can have functions and data members

  • Can be instantiated


18
New cards

What is a class diagram?

A diagram that summarizes what classes exist, what data they contain, what functions, and how they are related.

19
New cards
<p>Identify what each of the following represent:</p>

Identify what each of the following represent:

BankAccount = The Class Name

std::string owner and double balance = Data

deposit(double amount) and withdraw(double amount) = Functions

<p>BankAccount = The Class Name</p><p>std::string owner and double balance = Data</p><p>deposit(double amount) and withdraw(double amount) = Functions</p>
20
New cards

Classify each of these from Weakest to Strongest:

Dependency, Aggregation, and Association.

Dependency = The Weakest Relationship

Association = Weak, but slightly stronger than dependency

Aggregation = The Strongest Relationship

21
New cards

Explain why this is an example of Dependency.

class Printer {
    public:
    void print() {
        std::cout << "Printing\n";
    }
};

class Report{
    public:
// Recall: Printer& means printer is a reference to 
// the Printer object that gets passed in.
    void generate(Printer& printer) { 
        printer.print();
    }
};

int main() {
  Printer printer;
  Report report;
  report.generate(printer);
  return 0;
}


This is Dependency because Report does not store a printer. Printer only appears as a parameter. This means that Report only temporarily uses a Printer. It doesn’t store a Printer, and it only needs one when the function is running.

Therefore, Report does not contain a printer, it just uses one. This is an A uses B relationship.

22
New cards

Explain why this is an example of Association.

class Engine {
};

class Car {
    private:
// Pointer to an engine. Holds the address
// of an actual engine.
    Engine* engine;
};


This is Association because Car has an Engine. It uses a member variable, which means that Car actually stores an engine, and that the engine lasts so long as the car object exists.

23
New cards

Explain why this is an example of Aggregation.

class Engine {
    public:
    void start() {
        std::cout << "Engine started" << std::endl;
    }
};

class Car {
    private:
    Engine* engine;

    public:
    Car(Engine* e)
    :engine(e)
    {
    }

    void start() {
        engine->start();
    }
};

int main() {
    Engine engine;
    Car car(&engine);
    car.start();
    return 0;
}


This is Aggregation because Car stores an Engine, but it didn’t create engine, main() created engine, so main() owns it. The Engine can exist without car, because even if the car disappears, the engine still exists. Therefore, this is an example of Aggregation, where A has B, but doesn’t own B, and B can still exist independently of A.

24
New cards

Give a simple definition for what Dependency, Association, and Aggregation mean and how you can recognize them.

Dependency:

  • Means A uses B.

  • Dependency describes a temporary use relationship in which a change to one class might affect the other class that uses it.

  • You can recognize dependency when B is only used as a parameter.


Association:

  • Means A has a B.

  • Association is a connection between 2 classes where one object knows about and references another.

  • You can recognize association when B is stored as a member variable.


Aggregation:

  • Means A has B, but B can still exist independently of A.

  • Aggregation is an association where one object belonging to a different class is stored, but this object can still exist independently of the other class.

  • You can recognize aggregation when B is stored as a member variable, but was not created by A.


25
New cards

Is this Association, Dependency, or Aggregation? Why?

class Student {
};

class Course {
    private:
    Student* student;

    public:
    Course(Student* s)
    :student(s)
    {
    }
};


This is Aggregation, because Course contains Student, but Student can still exist independently of Course. Course does not own Student.

26
New cards
  1. Identify the relationship:

    class Car;
    
    class Mechanic {
        public:
        void repair(Car& car);
    };


This is an example of Dependency, because Mechanic only contains a reference to a Car object, but it does not actually contain an instance of a car. It only uses a Car temporarily in its repair() method, but it doesn’t form a permanent relationship to Car.

27
New cards
  1. Identify the relationship:

    class Engine {
    };
    
    class Car {
        private:
        Engine engine;
    };


This is an Association, because Car contains an Engine, and Car cannot exist without an Engine, although the Engine can exist independently of the Car.

28
New cards
  1. Convert this UML Diagram into code:


  2. __________Student_____________

  3. name: String

  4. gpa: double
    _____________________________

  5. study()

  6. display()


class Student {
    private:
    std::string name;
    double gpa;

    public:
    void study();
    void display();
    Student(std::string n, double g);
};

Student::Student(std::string n, double g) 
    :name(n), gpa(g)
    {
    }

OR

class Student {
    private:
    std::string name;
    double gpa;

    public:
    void study();
    void display();
    Student(std::string n, double g)
    :name(n), gpa(g)
    {
    }
};


29
New cards
  1. Create two classes: Library and Book such that:

  • Library stores a pointer to a Book

  • The Book is created in main()

  • The Book is passed into the Library

  • Then identify what type of relationship this is and why.


class Library {
    private:
    Book* bookptr;

    public:
    Library(Book* book)
    :bookptr(book)
    {
    }
};

class Book {
};

int main() {
    Book myBook;
    Library myLibrary(&myBook);
    return 0;
}

This is an example of Aggregation, because the book and the library can both exist independently of each other. Library does not own myBook, myBook is owned by main(). If myLibrary were to disappear, myBook would continue to exist.

30
New cards

What is composition?

Composition means one object is made up of another object. If the outer object is destroyed, then the inner object is destroyed too. The lifetime of the smaller object depends on the larger object.

31
New cards

What makes this an example of Composition?

class Engine {
};

class Car {
    private:
    Engine engine;
};


This is Composition because we are not storing a pointer to an engine, like in Aggregation. Here, we actually store an Engine object inside of the Car class. Meaning that the engine is part of the car class itself.

If you were to say: Car myCar, then C++ would automatically create Engine engine inside. You would not need to create an engine separately. Also, if our myCar object were to be destroyed, this means that our engine would be destroyed along with it.

32
New cards

Explain the difference between Aggregation and Composition.

In Aggregation, one class contains an object of another class, or a pointer to an object of another class, but that object can still exist independently of the class that contains it.

On the other hand, in Composition, an object of one class becomes part of another class, and the two cannot be separated. The object is not independent of the class that contains it.

33
New cards

Which is Aggregation and which is Composition?

Book myBook;
Library myLibrary(&myBook);
class Library {
    private:
    Book book;
};


// Aggregation
Book myBook;
Library myLibrary(&myBook);

Here, Library contains a Book, but myBook can still exist independently of Library. If myLibrary disappears, myBook will continue to exist. Therefore, it is Aggregation

// Composition
class Library {
    private:
    Book book;
};

On the other hand, here, Library contains book as part of the Library itself. If we were to instantiate a Library object, then a Book object would automatically be created with it. If we destroy the Library object, then we destroy the book object too. Therefore, this is Composition.

34
New cards

What is inheritance?

When one class is a sub-type of another class.

For example:

class Vehicle{
};

class Bicycle : public Vehicle {
};

Here, Bicycle is a Vehicle.

35
New cards

What will be the output of the following code?

#include <iostream>

class Engine {
public:

    Engine() {
        std::cout << "Engine created\n";
    }

    ~Engine() {
        std::cout << "Engine destroyed\n";
    }
};

class Car {
private:

    Engine engine;

public:

    Car() {
        std::cout << "Car created\n";
    }

    ~Car() {
        std::cout << "Car destroyed\n";
    }
};

int main() {

    Car myCar;

    return 0;
}


Engine created

Car created

Car destroyed

Engine destroyed

36
New cards

Explain the highlighted line in the following example:

class Vehicle {
    public:
    virtual void accelerate() = 0;
};

class Bicycle : Vehicle {
    public:
    void accelerate() override {
        std::cout << "PEDAL FASTER";
    }
};

int main() {
    Bicycle my_bike;
    
    // EXPLAIN THIS LINE
    Vehicle& my_vehicle = my_bike;
    my_vehicle.accelerate();
    return 0;
}


    Vehicle& my_vehicle = my_bike;

This means that my_vehicle is a reference to a Vehicle object, my_bike. So now my_vehicle and my_bike can be used interchangeably because they are references to each other.

37
New cards

What type is this demonstrating? Dependency, Association, Aggregation, or Composition?

class Keyboard {
};

class Computer {
    private:
    Keyboard keyboard;
};


This is Composition, because Computer contains Keyboard as part of the class itself. If we were to create a Computer object, this would automatically instantiate a Keyboard object as well. If the Computer object were to be destroyed, then the Keyboard object would be destroyed too.

38
New cards

What type is this demonstrating? Dependency, Association, Aggregation, or Composition?

class Student {
};

class Classroom {
    private:
    Student* student;

    public:
    Classroom(Student* s)
    :student(s)
    {
    }
};


This is Aggregation. Because classroom contains a pointer to a Student, but a Student can still exist independently of Classroom.

39
New cards

What is Realization?

Realization means that a concrete class provides implementation for the functions required by an interface or abstract class.

40
New cards

Which part of the following example demonstrates Realization?

class Printable {
    public:
    virtual void print() const = 0;
    virtual ~Printable(){}
};
class Book : Printable {
    private:
    std::string title;

    public:
    Book(std::string t)
    :title(t)
    {
    }

    void print() const override {
        std::cout << title << std::endl;
    }
};


Only the part where Book provides an implementation for the print() function.

class Book : Printable {
    private:
    std::string title;

    public:
    Book(std::string t)
    :title(t)
    {
    }

// This is the only part that represents Realization
    void print() const override {
        std::cout << title << std::endl;
    }
};


41
New cards

Which part of the following example demonstrates Realization? And what does the highlighted line do?

// The Creature class
class Creature {
    public:
    virtual void allInfo() const = 0;
};

// The Person class
class Person : public Creature {
    public:

    Person(const std::string& name)
    :name(name)
    {
    }

    void allInfo() const override {
        std::cout << "My name is " << name;
    }

    protected:
    std::string name;
};

// Main
int main() {
// EXPLAIN WHAT THIS LINE IS DOING
    Creature* favaBeanMuncher = new Person("Hannibal Lecter");
    favaBeanMuncher->allInfo();
}


Only this section demonstrates realization:

    void allInfo() const override {
        std::cout << "My name is " << name;
    }

This line creates a Creature pointer that actually points to a Person object. This is why doing:
favaBeanMuncher→allInfo() will actually just run the underlying function belonging to Person.

42
New cards

Which of these is Realization, and which is Inheritance?

class Bicycle : public Vehicle {
    public:
    void accelerate() override {
    }
};


Inheritance

class Bicycle : public Vehicle {
};

Realization

class Bicycle : public Vehicle {
    public:

// ONLY THIS PART
    void accelerate() override {
    }
};


43
New cards
<p>What kind of relationship does this symbol represent?</p>

What kind of relationship does this symbol represent?

Association/Generalization

Note: Generalization is just a synonym for Association

<p>Association/Generalization</p><p>Note: Generalization is just a synonym for Association</p>
44
New cards
<p>What kind of relationship does this symbol represent?</p>

What kind of relationship does this symbol represent?

Inheritance

<p>Inheritance</p>
45
New cards
<p>What kind of relationship does this symbol represent?</p>

What kind of relationship does this symbol represent?

Realization

46
New cards
<p>What kind of relationship does this symbol represent?</p>

What kind of relationship does this symbol represent?

Dependency

<p>Dependency</p>
47
New cards
<p>What kind of relationship does this symbol represent?</p>

What kind of relationship does this symbol represent?

Aggregation

<p>Aggregation</p>
48
New cards
<p>What kind of relationship does this symbol represent?</p>

What kind of relationship does this symbol represent?

Composition

<p>Composition</p>
49
New cards
<p>What does this diagram represent?</p>

What does this diagram represent?

It represents a composition relationship where a car is composed of carburetors.

We also know that the car is composed of one and only one carburetor, while the carburetor can belong to 0 or 1 car.

50
New cards
<p>What does this diagram represent?</p>

What does this diagram represent?

It represents a dependency relationship where a car is dependent on a wheel, or in other words, a car uses a wheel.

51
New cards
<p>What does this diagram represent?</p>

What does this diagram represent?

It represents an aggregate relationship where professor is an aggregate of a class. A professor has one or more classes, while a class can only belong to one and only one professor.

52
New cards
<p>What does this diagram represent?</p>

What does this diagram represent?

It represents an Inheritance relationship where Student and Professor inherit from Person. So both Professor and Student are subclasses of Person.

53
New cards

What are Design Idioms about? Why are they important?

Design Idioms are about how classes are organized. They’re important because they allow code to be easily modifiable, which matters a lot when designing software.

54
New cards

What does Coupling describe?

Coupling describes how dependent classes are on each other.

It measures how many other classes are likely to break if you change one class.

55
New cards

Is this an example of low coupling or tight coupling?

class Printer {
    public:
    void print() {
        std::cout << "Printing";
    }
};

class Report {
    public:
    void generate(Printer& printer) {
        printer.print();
    }
};


This is an example of low coupling/loose coupling because the two classes are minimally dependent on each other. Report only needs a single thing: print() from Printer. Even if we were to completely redesign Report and add methods like generatePDF() or generateHTML(), it’s highly unlikely to affect Printer. Likewise, if you were to add multiple new methods to Printer, it’s highly unlikely to affect anything in Report. Therefore, we can say they have low coupling because changes in one class only minimally affect the other.

56
New cards

Why is loose coupling good?

Loose coupling is good because it allows you to only focus on one class when debugging. You don’t need to understand 25 other unrelated classes in order to be able to fix a single class.

57
New cards

Is this an example of low coupling or tight coupling?

class Engine {
    public:
    int horsepower;
};

class Car {
    public:
    Dashboard* dashboard;
};

class Transmission {
    public:
    Engine* engine;
};

class Dashboard {
    public:
    Transmission* transmission;
};


This is an example of high coupling because all the classes are interconnected. If we suddenly decide to change Engine, we suddenly also need to change Car and Transmission, because they all rely on each other. The changes spread everywhere because the classes are so tightly coupled.

58
New cards

Why is this called the Diamond Problem?

class A {
};

class B : public A {
};

class C : public A {
};

class D : public B, public C {
};


Because the inheritance patterns form a diamond!

B inherits from A, C also inherits from A, and D inherits from both B and C. Ultimately, this forms a diamond shape:

<p>Because the inheritance patterns form a diamond!</p><p>B inherits from A, C also inherits from A, and D inherits from both B and C. Ultimately, this forms a diamond shape:</p>
59
New cards

Why is the Diamond Problem a problem? Reference the following:

class A {
  int x;
};

class B : public A {
};

class C : public A {
};

class D : public B, public C {
};


The Diamond Problem is a problem because it creates ambiguity.

For example, if we were to do:

D myD;
myD.x;

The compiler gets confused, it doesn’t know whether you want to access the x inherited from B or C. It’s the reason why we need to be careful with multiple inheritance.

60
New cards

What is the solution to the Diamond Problem?

class A {
  int x;
};

class B : public A {
};

class C : public A {
};

class D : public B, public C {
};


The solution is to use virtual inheritance, because virtual inheritance tells C++ that even if multiple classes inherit from A, only one copy of A should be shared. That way, there is only a single version of x that can be accessed from A, because there is only a single copy of A that exists among both classes.

class A {
  int x;
};

class B : virtual public A {
};

class C : virtual public A {
};

class D : public B, public C { 
// does not need to be virtual because it inherits from two completely different classes (not the same class)
};


61
New cards

What is Cohesion about?

Cohesion is about how focused a class is on a single responsibility.

62
New cards

Does the following example have high cohesion or low cohesion?

class BankAccount {
    private:
    double balance;

    public:
    void deposit();
    void withdraw();
    double getBalance();
};


This example has high cohesion because it is very focused. Everything is about Bank accounts and the class only has a single job.

63
New cards

Does the following example have high cohesion or low cohesion?

class Tasks {
    public:
    void depositMoney();
    void drawGraphics();
    void playMusic();
    void sendEmail();
};


This has low cohesion because the class has no real purpose, no single responsibility.

64
New cards

What does method-level cohesion describe?

Method level cohesion just describes how focused a method is on a single task.

For example, here’s a method with high cohesion:

void saveCharacter() {}

Here’s a method with low cohesion:

void saveCharacterAndPlaySoundAndPrintReport() {}


65
New cards

What is the problem with this? What is this example demonstrating?

class Student {
    public:
    void all_info() {
        std::cout << "Student info\n";
    }
};

class Mathematician {
    public:
    void all_info() {
        std::cout << "Mathematician info\n";
    }
};

class MathStudent : public Student, 
public Mathematician {
};

int main() {
  MathStudent bob;
  bob.all_info();
}


This is demonstrating Multiple Inheritance Ambiguity. The reason this is a problem is because it creates ambiguity surrounding which all_info() method we want to access.

66
New cards

What can we do to eliminate all ambiguity here?

class Student {
    public:
    void all_info() {
        std::cout << "Student info\n";
    }
};

class Mathematician {
    public:
    void all_info() {
        std::cout << "Mathematician info\n";
    }
};

class MathStudent : public Student, 
public Mathematician {
};

int main() {
  MathStudent bob;
  bob.all_info();
}


We must use the scope resolution operator to specify which implementation of the all_info() method we want to use in main.

What is the problem with this? What is this example demonstrating?

int main() {
  MathStudent bob;
  bob.mathematician::all_info();
}

This version explicitly tells it to use the Mathematician version.

67
New cards

What do we want to maximize and what do we want to minimize when it comes to Coupling and Cohesion?

We want to maximize Cohesion and minimize Coupling.

Remember that Cohesion describes how focused a class is, and Coupling describes how interconnected/dependent classes are on each other. The idea is that we want to minimize Coupling so that changing one class doesn’t break 10 others, and we want to maximize Cohesion so that every method has one clear responsibility.

68
New cards

Suppose changing Customer requires modifying Order, Invoice, Payment, Shipping, and Notification. Does this suggest loose coupling or high coupling? Why?

This suggest Tight/High Coupling because it shows that classes are highly dependent on each other, and that making a change in one class is very likely to break many others.

69
New cards

Why is this an example of bad design?

class Fish {
    private:
    std::string fishType;
};

class Rice {
    private:
    std::string riceType;
};

class Sushi {
    private:
    Rice rice;
    Fish fish;
};


This is bad design because every Sushi object is forced to contain fish. This means that something like a cucumber or avocado roll would not work. The design is very rigid.

70
New cards

What is Decoupling?

Decoupling is about ways of reducing dependency strength. Note that it is not about removing dependencies between classes, just making them weaker.

71
New cards

Why is this an example of better design?

class Filling {
    public:
    virtual ~Filling(){}
};

class Fish : public Filling {
};

class Cucumber : public Filling {
};

class Avocado : public Filling {
};

class Sushi {
    private:
    std::vector<Filling*> fillings;
};


This is better because Sushi is no longer dependent on Fish. Sushi only depends on Filling, which means it could take on any of the values that extend Filling: Cucumber, Avocado, and Fish.

72
New cards

Is this an example of tight coupling or loose coupling?

class TextFile {
    public:
    void read(){
    }

    void write(){
    }
};

class UI {
    private:
    TextFile file;
};


This is an example of tight coupling, because UI is highly dependent on TextFile. This is bad because it means that changing TextFile to a different type, like SQLite, would require changes to both TextFile and UI.

73
New cards

Why is this an example of better design?

class DataSource {
    public:
    virtual void read() = 0;
    virtual void write() = 0;
    virtual ~DataSource(){}
};

class TextFile : public DataSource {
    public:
    void read() override {
        std::cout << "Reading text file";
    }

    void write() override {
        std::cout << "Writing text file";
    }
};

class SQLiteDB : public DataSource {
    public:
    void read() override {
        std::cout << "Reading database";
    }

    void write() override {
        std::cout << "Writing database";
    }
};

class UI {
    private:
    DataSource* dataSource;
    public:
    UI(DataSource* ds)
    :dataSource(ds){}
};


This is an example of better design because it makes use of interfaces and abstraction in order to avoid making the UI class dependent on a single type of DataSource. Now all the DataSources are easily interchangeable, and changing one will not affect UI.

74
New cards

Would you describe the dependency between Car and Engine as being relatively weak or relatively strong?

class Engine {
};

class Car {
    private:
    Engine engine;
};


The dependency is strong, because any change to Engine will directly affect Car, and Car cannot exist without an Engine.

75
New cards

Suppose we have:

class PDFFile {
};

class ReportViewer {
    private:
    PDFFile file;
};

Tomorrow the company decides that they want to support Word documents, Excel files, and HTML reports. Would this design be easy or difficult to extend? Why?

This design would be difficult to extend, because right now, ReportViewer directly relies on PDFFile. Changing PDFFile to something else would force a change in ReportViewer as well.

76
New cards

What is the purpose of using SOLID Design Principles?

SOLID Design Principles exist for the purpose of helping us achieve high cohesion and loose coupling.

77
New cards

What does each part of SOLID stand for?

S = Single Responsibility Principle

O = Open/Closed Principle

L = Liskov Substitution Principle

I = Interface Segregation Principle

D = Dependency Inversion Principle

78
New cards

What does the Single Responsibility Principle state?

It states that a class should only have a single clear responsibility. Essentially, it states that a class should have high cohesion.

79
New cards

Is this a good or bad example of the Single Responsibility Principle?

class User {
    private:
    std::string user;
    std::string password;

    public:
    void createPost() {
    }

    void connectToDatabase() {
    }

    void savePost() {
    }
};


This is a bad example of the Single Responsibility Principle. This is because the User class doesn’t have a single clear responsibility. We are unsure if it is supposed to represent a single person, a database connection, or a forum posting system, because the reality is that it does all 3 at once. Too many responsibilities have been condensed into a single class.

80
New cards

Is this a good or bad example of the Single Responsibility Principle?

class User {
    private:
    std::string username;
    std::string password;
};

class PostWriter {
    public:
    void createPost(){}
};


This is a good example of the Single Responsibility Principle (SRP) because now the User class only has a single clear responsibility, of storing a username and a password, and likewise, the PostWriter class only has a single responsibility of creating a post.

81
New cards

Is it okay if we have a semicolon here instead of curly braces?

class User {
    private:
    std::string username;
    std::string password;
};

class PostWriter {
    public:
    void createPost(); // semicolon instead of {}
};


Yes it is perfectly fine to use either a semicolon or curly braces here.

82
New cards

What does the Open/Closed Principle state?

The Open/Closed principle states that classes are open to extension, but closed for modification. This means that you shouldn’t keep editing existing code to support new use cases, you should create entirely new classes.

83
New cards

Does this violate the OCP (Open/Closed Principle)?

class UIWidget {
    private:
    std::string type;
    
    public:
    void draw() {
        if(type == "Button") {
            std::cout << "Draw Button";
        } else {
            std::cout << "Draw Panel";
        }
    }
};


Yes, this does violate the Open/Closed principle, because the moment you want to add new features to the UI system, like TextBoxes, ScrollBars, Labels, etc, you end up having to add many new else if statements to draw().

Eventually, void draw() gets so long and full of else if statements that the method keeps on growing forever, so errors accumulate and it becomes harder to test.

84
New cards

Does this violate the OCP (Open/Closed Principle)?

class UIWidget {
    public:
    virtual void draw() = 0;
    virtual ~UiWidget(){};
};

class UIButton : public UIWidget {
    public:
    void draw() override {
        std::cout << "Drawing button";
    }
};


This does not violate the Open/Closed Principle, because here, the UIWidget class is easily extendable. We aren’t modifying existing code to add new functionality, we are just creating new classes for it, which is in direct support of the Open/Closed Principle.

85
New cards

Rewrite this using inheritance and an interface.

class Animal {
    public:
    void makeSound() {
        if(type == "Dog"){
        } else if(type == "Cat") {
        }
    }
};


class Animal {
    public:
    virtual void makeSound() = 0;
    virtual ~Animal() {}
};

class Dog : public Animal {
    public:
    void makeSound() override {
        std::cout << "Woof";
    }
};

class Cat : public Animal {
    public:
    void makeSound() override {
        std::cout << "Meow";
    }
};


86
New cards

What does the Liskov Substitution Principle state?

It states that objects of a base type should be replaceable with objects of a derived type.

87
New cards

Does this violate or support the Liskov Substitution Principle?

class Canvas {
    private:
    UIButton* button;
    UITextBox* textbox;
    UIScrollbar* scrollbar;

    public:
    Canvas(UIButton* button, UITextBox* textbox) {
        this->button = button;
        this->textbox = textbox;
    }

    void drawScreen() {
        button->draw();
        textbox->draw();
    }
};

class UIWidget {
    public:
    virtual void draw() = 0;
};

class UIButton : public UIWidget {
    public:
    void draw() override {
        std::cout << "Drawing button";
    }
};

class UITextBox : public UIWidget {
    public:
    void draw() override {
        std::cout << "Drawing TextBox";
    }
};


This does not violate the Liskov Substitution Principle, because it allows us to substitute members of a base class with members of a derived class, for example:

UIWidget* widget;
widget = new UIButton();
widget = new UITextBox();

However, it does violate the Open/Closed principle and demonstrates bad architectural design because it has tight coupling. If you wanted to add new features to the UI, like a scrollbar or search bar, you would need to modify existing code instead of creating new classes.

88
New cards

What does the Interface Segregation Principle state?

The Interface Segregation Principle states that interfaces should be small and focused so that classes are not forced to implement unnecessary methods.

89
New cards

Does this violate or support the Interface Segregation Principle?

class Animal {
    public:
    virtual void walk() = 0;
    virtual void swim() = 0;
    virtual void fly() = 0;
};


This violates the Interface Segregation Principle because it forces all subclasses of Animal to implement unnecessary methods that are not necessarily applicable.

For example:

class Animal {
    public:
    virtual void walk() = 0;
    virtual void swim() = 0;
    virtual void fly() = 0;
}

class Fish : public Animal {
};

class Human : public Animal {
};

Here, both Fish and Human are forced to implement fly, despite neither of them being able to fly.

90
New cards

Does this violate or support the Interface Segregation Principle?

class Walkable {
    public:
    virtual void walk() = 0;
};

class Swimmable {
    public:
    virtual void swim() = 0;
};

class Human : public Walkable, public Swimmable {
    public:
    virtual void swim() = 0;
};


This supports the Interface Segregation Principle because it creates smaller, more focused interfaces, and doesn’t force subclasses to implement unnecessary behavior.

91
New cards

What does the Dependency Inversion Principle state?

The Dependency Inversion Principle states that classes should depend on Interfaces and abstractions, not on classes focused on low level implementation detail.

Or in other words:
High level classes should not depend on low-level classes. Both should depend on abstractions.

92
New cards

Does this violate or support the Dependency Inversion Principle?

class UI {
    private:
    TextFile file;
};

class TextFile {
    public:
    void read();
    void write();
};


This violates the Dependency Inversion principle because TextFile is a low-level, detail implementation class, and UI, which is a higher level, more complex class depends on it instead of an interface.

93
New cards

Does this violate or support the Dependency Inversion Principle?

class DataSource {
    public:
    virtual void read() = 0;
    virtual void write() = 0;
    virtual ~DataSource() {}
};

class TextFile : public DataSource {
};

class SQLite : public DataSource {
};

class UI {
    private:
    DataSource* dataSource;
};


This supports the Dependency Inversion Principle, because now the high-level class, UI, only depends on the DataSource interface rather than the low-level implementation detail, TextFile.

94
New cards

Summarize what SOLID stands for, and what each part of the acronym means.

S = Single Responsibility Principle: One class should only have a single clear responsibility

O = Open/Closed Principle: Classes are open for extension, but closed for modification. New classes should be added in order to add new features.

L = Liskov Substitution Principle: Objects of a base class should be replaceable with objects of a derived type.

I = Interface Segregation Principle: Interfaces should be small and focused, and they should not force classes to implement unnecessary behavior/methods.

D = Dependency Inversion Principle: High-level classes should never depend on low-level implementation details. Both low-level and high-level classes should depend on interfaces and abstractions.

95
New cards

What does the Law of Demeter state?

The Law of Demeter states that classes and objects should only have limited knowledge about other classes, and should only interact with their immediate friends.

96
New cards

Let’s say we have 3 classes: Cashier, Customer, and Wallet.

Suppose the Cashier wants $20, and we do:

Customer customer;
customer.getWallet().getMoney(20);

Why is this a problem?

This is a problem because the Cashier is reaching through multiple objects in order to access the customer’s money. The cashier already knows too much information about the Customer that they should not have access to. The Cashier should not have access to information about how the Customer stores their money, whether or not the customer even has a Wallet, what functions the Wallet contains, etc.

This is also a problem because it significantly increases coupling. Now Cashier depends on both Customer and Wallet.

97
New cards

Does this example violate the Law of Demeter?

class Customer {
    private:
    Wallet wallet;

    public:
    Customer(int money)
    :wallet(money)
    {
    }

    int getMoney(int amount) {
        return wallet.getMoney(amount);
    }
};

class Cashier {
    public:
    void takeMoney(Customer& customer) {
        int money = customer.getMoney(20);
        std::cout << money;
    }
};


No, it supports the Law of Demeter because the Cashier is no longer reaching into the Customer’s wallet in order to grab their money.

The Cashier has limited information about the Customer and their method of payment, and the Cashier can only access money through the Customer.

98
New cards

What is a Train Wreck?

A Train Wreck is when you have a huge chain of calls, for example:

obj.getX().getY().getZ().getAll();


99
New cards

Why are Train Wrecks bad?

Train Wrecks are bad because they force us to reach through other objects in order to access data, and they dramatically increase coupling, making it very easy to break an entire class just by breaking the chain.