# CSE332S 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 *); }; ```