# CSE332S Object-Oriented Programming in C++ (Lecture 17)

## Object Oriented Programming Building Blocks

OOP Building Blocks for Extensible, Flexible, and Reusable Code

Today: Techniques Commonly Used in Design Patterns

- **Program to an interface** (last time)
- **Object composition and request forwarding** (today)
  - Composition vs. inheritance
- **Run-time relationships between objects** (today)
  - Aggregate vs. acquaintance
- **Delegation** (later...)

Next Time: Design Patterns

Describe the core of a repeatable solution to common design problems.

### Code Reuse: Two Ways to Reuse a Class

#### Inheritance

Code reuse by inheriting the implementation of a base class.

- **Pros:**
  - Inheritance relationships defined at compile-time - simple to understand.
- **Cons:**
  - Subclass often inherits some implementation from superclass - derived class now depends on its base class implementation, leading to less flexible code.

#### Composition

Assemble multiple objects together to create new complex functionality, forward requests to the responsible assembled object.

- **Pros:**
  - Allows flexibility at run-time, composite objects often constructed dynamically by obtaining references/pointers to other objects (dependency injection).
  - Objects known only through their interface - increased flexibility, reduced impact of change.
- **Cons:**
  - Code can be more difficult to understand, how objects interact may change dynamically.

### Example: Our First Design Pattern (Adapter Pattern)

**Problem:** We are given a class that we cannot modify for some reason - it provides functionality we need, but defines an interface that does not match our program (client code).

**Solution:** Create an adapter class, adapter declares the interface needed by our program, defines it by forwarding requests to the unmodifiable object.

Two ways to do this:

```cpp
class unmodifiable {
public:
    int func(); // does something useful, but doesn’t match the interface required by the client code
};
```

1. **Inheritance**

   ```cpp
   // Using inheritance:
   class adapter : protected unmodifiable {
    // open the access to the protected member func() for derived class
   public:
       int myFunc() {
           return func(); // forward request to encapsulated object
       }
   };
   ```

2. **Composition**

   ```cpp
   class adapterComp {
       unmodifiable var;
   public:
       int myFunc() {
           return var.func();
       }
   };
   ```

### Thinking About and Describing Run-time Relationships

Typically, composition is favored over inheritance! Object composition with programming to an interface allows relationships/interactions between objects to vary at run-time.

- **Aggregate:** Object is part of another. Its lifetime is the same as the object it is contained in. (similar to base class and derived class relationship)
- **Acquaintance:** Objects know of each other, but are not responsible for each other. Lifetimes may be different.

```cpp
// declare Printable Interface
// declare printable interface
class printable {
    public:
    virtual void print(ostream &o) = 0;
};
// derived classes defines printable
// interface
class smiley : public printable {
    public:
    virtual void print(ostream &o) {
        o << ":)";
    };
};
// second derived class defines
// printable interface
class frown : public printable {
    public:
        virtual void print(ostream &o) {o << ":(";
    };
};
```

1. **Aggregate**

```cpp
// implementation 1:
// Aggregate relationship
class emojis {
printable * happy;
printable * sad;
public:
emojis() {
happy = new smiley();
sad = new frown();
};
~emojis() {
delete happy;
delete sad;
};
};
```

2. **Acquaintance**

```cpp
// implementation 2:
// Acquaintances only
class emojis {
printable * happy;
printable * sad;
public:
emojis();
~emojis();
// dependency injection
void setHappy(printable *);
void setSad(printable *);
};
```
