upgrade structures and migrate to nextra v4

This commit is contained in:
Zheyuan Wu
2025-07-06 12:40:25 -05:00
parent 76e50de44d
commit 717520624d
317 changed files with 18143 additions and 22777 deletions

View File

@@ -0,0 +1,147 @@
# 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 doesnt 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 *);
};
```