-
Notifications
You must be signed in to change notification settings - Fork 1
Add 01 class #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add 01 class #47
Changes from 1 commit
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
d2faa0d
Add 01 class
snovalnikov 3f753d7
Update legacy_code/01-Sprout_wrap.md
snovalnikov 6e89fe8
Update legacy_code/01-Sprout_wrap.md
snovalnikov fe3ffe7
Update legacy_code/01-Sprout_wrap.md
snovalnikov a75dcf9
Add 01 class
snovalnikov 685babf
Merge branch 'feature/Add_sprout_and_wrap' of https://github.com/Codi…
snovalnikov 83cbba6
Merge branch 'main' into feature/Add_sprout_and_wrap
megabyde File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| ### Exercise 1: Sprout a Simple Class | ||
|
|
||
| - **Objective:** Implement the Sprout Method on a simple class. | ||
| - **Task:** | ||
| - Create a `Car` class with a `drive()` method that prints `"The car is driving!"`. | ||
| - Use the **Sprout Method** to add new functionality (e.g., adding a `fuel()` method) to the `Car` | ||
| class, **without modifying** the original `drive()` method. | ||
| - Add a `fuel()` method that prints `"Refueling the car!"`. | ||
| - Extend the `Car` class to call both `drive()` and `fuel()` in the new method | ||
| `drive_with_fuel()`. | ||
|
|
||
| - **Hint:** | ||
| - Focus on isolating the changes to the `drive_with_fuel()` method, while keeping the original | ||
| `drive()` method intact. | ||
|
|
||
| - **Time Limit:** 5-10 minutes | ||
|
|
||
| --- | ||
|
|
||
| ### Exercise 2: Implement a Wrap Method | ||
|
|
||
| - **Objective:** Apply the **Wrap Method** to enhance functionality. | ||
| - **Task:** | ||
| - Create a `Coffee` class with a `cost()` method that prints `"Basic coffee: $5"`. | ||
| - Implement a **Wrap Method** where a new class, `MilkDecorator`, wraps the `Coffee` class to | ||
| modify its behavior by adding a cost for milk. | ||
| - The `MilkDecorator` class should call the original `cost()` method of `Coffee` and then add an | ||
| additional cost for milk. | ||
| - After that, create a `MilkDecorator` instance and call the `cost()` method to see the new | ||
| behavior. | ||
|
|
||
| - **Time Limit:** 5-10 minutes | ||
|
|
||
| --- | ||
|
|
||
| ### Exercise 3: Wrapping Multiple Behaviors | ||
|
|
||
| - **Objective:** Apply the **Wrap Method** to chain multiple decorators. | ||
| - **Task:** | ||
| - Building on the previous exercise, extend the `MilkDecorator` to create a `SugarDecorator`. | ||
| - The `SugarDecorator` should add an additional cost for sugar, while still keeping the | ||
| `MilkDecorator` functionality. | ||
| - Create a `Coffee` object and wrap it with both `MilkDecorator` and `SugarDecorator`. | ||
| - Use the `cost()` method to see the total cost after both decorators are applied. | ||
|
|
||
| - **Time Limit:** 5-10 minutes | ||
|
|
||
| --- | ||
|
|
||
| ### Exercise 4: Python Decorator for Wrap Method | ||
|
|
||
| - **Objective:** Implement a **decorator function** in Python to wrap a method. | ||
| - **Task:** | ||
| - Create a `Car` class with a `drive()` method that prints `"The car is driving!"`. | ||
| - Write a **Python decorator function** that adds functionality to the `drive()` method. | ||
| For instance, before calling the `drive()` method, the decorator could print | ||
| `"Starting the car..."`. | ||
| - Apply the decorator function to the `drive()` method and call it. | ||
|
|
||
| - **Time Limit:** 5-10 minutes | ||
|
|
||
| --- | ||
|
|
||
| ### Exercise 5: Refactor with Decorators | ||
|
|
||
| - **Objective:** Refactor an existing method to use a decorator for extra functionality. | ||
| - **Task:** | ||
| - Given a simple `User` class with a `login()` method that prints `"User logged in"`, write | ||
| a decorator that logs an additional message such as `"Tracking login time..."` whenever the | ||
| `login()` method is called. | ||
| - Apply the decorator to the `login()` method and test it. | ||
|
|
||
| - **Time Limit:** 5-10 minutes | ||
|
|
||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,288 @@ | ||
| --- | ||
| title: Working Effectively with Legacy Code | ||
| subtitle: Chapter 6 – I Need to Make a Change. What Can I Do? | ||
| ... | ||
|
|
||
| # Pre-work | ||
|
|
||
| - Book: *Working Effectively with Legacy Code* by Michael Feathers | ||
| - Focus: Chapter 6 change techniques | ||
|
snovalnikov marked this conversation as resolved.
Outdated
|
||
| - Optional: <https://michaelfeathers.silvrback.com/the-sprout-method> | ||
|
|
||
| # Chapters | ||
|
|
||
| ::: columns | ||
|
|
||
| :::: column | ||
|
|
||
| | Chapter | Time | | ||
| | ---------------------------| -------- | | ||
| | Warmup | 00:00:00 | | ||
| | Sprout Method | 00:05:00 | | ||
| | Sprout Class | 00:15:00 | | ||
| | Wrap Method | 00:25:00 | | ||
|
|
||
| :::: | ||
|
|
||
| :::: column | ||
|
|
||
| | Chapter | Time | | ||
| | ---------------------------| -------- | | ||
| | Wrap Class | 00:35:00 | | ||
| | Strategy Comparison | 00:45:00 | | ||
| | Summary & Q&A | 00:55:00 | | ||
|
|
||
| :::: | ||
|
|
||
| ::: | ||
|
|
||
| # Timetable | ||
|
|
||
| | Activity | Time | | ||
| | ----------------------------| ------ | | ||
| | Warmup & Goals | 5 min | | ||
| | Sprout Method & Class | 20 min | | ||
| | Wrap Method & Class | 20 min | | ||
| | Compare & Discuss | 5 min | | ||
| | Summary & Q&A | 10 min | | ||
|
|
||
| # Warmup | ||
|
|
||
| - What’s your current strategy when you need to change legacy code? | ||
| - Ever added a wrapper instead of modifying directly? | ||
| - Share in chat: "Sprout" or "Wrap" — which sounds safer to you? | ||
|
|
||
| \note{ | ||
| Use this to establish mental models and prepare learners to explore change-based techniques. | ||
| } | ||
|
|
||
| # Sprout Method | ||
|
|
||
| - Add new logic in a new method | ||
| - Call the new method from existing one | ||
| - No edits to original logic | ||
|
|
||
| ## Example | ||
|
|
||
| ```python | ||
| def charge(customer): | ||
| return new_charge_logic(customer) | ||
|
|
||
| def new_charge_logic(customer): | ||
| # New behavior | ||
| return 42 | ||
| ``` | ||
|
|
||
| # Sprout Class | ||
|
|
||
| - Create a new class for new behavior | ||
| - Keeps old logic intact | ||
| - Ideal for complex changes needing state | ||
|
|
||
| ## Example | ||
|
|
||
| ```python | ||
| def calculate_invoice(cust): | ||
| return InvoiceCalculator(cust).run() | ||
|
|
||
| class InvoiceCalculator: | ||
| def __init__(self, customer): | ||
| self.customer = customer | ||
| def run(self): | ||
| # New logic here | ||
| pass | ||
| ``` | ||
|
|
||
| # Wrap Method | ||
|
|
||
| - Create a method that wraps (delegates to) the old method | ||
| - Adds logic *before* or *after* the call | ||
| - Leaves original untouched | ||
|
|
||
| ## Example | ||
|
|
||
| ```python | ||
| def send_invoice(customer): | ||
| log_invoice(customer) | ||
| real_send_invoice(customer) | ||
|
|
||
| def real_send_invoice(customer): | ||
| print("Sending...") | ||
| ``` | ||
|
|
||
| # Wrap Method Benefits | ||
|
|
||
| - Low risk: no edits to legacy method | ||
| - Adds behavior transparently | ||
| - Good when you can’t or don’t want to touch legacy | ||
|
|
||
| # Wrap Class | ||
|
|
||
| - Create a new class that wraps the legacy class | ||
| - Delegates calls while injecting new logic | ||
| - Helps isolate change when subclassing is risky | ||
|
|
||
| ## Example | ||
|
|
||
| ```python | ||
| class EmailSenderWrapper: | ||
| def __init__(self, real_sender): | ||
| self.real = real_sender | ||
|
|
||
| def send(self, message): | ||
| self.log(message) | ||
| return self.real.send(message) | ||
|
|
||
| def log(self, msg): | ||
| print("Logging email:", msg) | ||
| ``` | ||
|
|
||
| # Decorators and the Wrap Method | ||
|
|
||
| - Decorators are a special form of wrapping that allows for dynamic extension. | ||
| - In the context of the Wrap Method, decorators provide an elegant solution for layering additional behavior. | ||
|
|
||
| ### C++ Decorator Pattern Example | ||
|
|
||
| ```cpp | ||
| #include <iostream> | ||
| #include <memory> | ||
|
|
||
| class Coffee { | ||
| public: | ||
| virtual void cost() const { std::cout << "Basic coffee: $5\n"; } | ||
| }; | ||
|
|
||
| class CoffeeDecorator : public Coffee { | ||
| protected: | ||
| std::unique_ptr<Coffee> coffee; | ||
| public: | ||
| CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {} | ||
| void cost() const override { | ||
| coffee->cost(); | ||
| } | ||
| }; | ||
|
|
||
| class MilkDecorator : public CoffeeDecorator { | ||
| public: | ||
| MilkDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {} | ||
| void cost() const override { | ||
| CoffeeDecorator::cost(); | ||
| std::cout << "Adding milk: $1\n"; | ||
| } | ||
| }; | ||
|
|
||
| int main() { | ||
| std::unique_ptr<Coffee> coffee = std::make_unique<Coffee>(); | ||
|
snovalnikov marked this conversation as resolved.
Outdated
|
||
| MilkDecorator decoratedCoffee(std::move(coffee)); | ||
|
snovalnikov marked this conversation as resolved.
Outdated
|
||
| decoratedCoffee.cost(); | ||
| } | ||
| ``` | ||
|
|
||
| \note{ | ||
| This C++ code shows how decorators wrap an object and add additional behavior. The `MilkDecorator` class extends the `Coffee` class without altering its original structure. | ||
| } | ||
|
|
||
| # Decorator Pattern in Python | ||
|
|
||
| - **Python**: A more dynamic and concise way to implement decorators. | ||
| - Can be used to modify methods or class behavior at runtime using functions or class-based decorators. | ||
|
|
||
| ### Python Decorator Example | ||
|
|
||
| ```python | ||
| class Coffee: | ||
| def cost(self): | ||
| print("Basic coffee: $5") | ||
|
|
||
| class CoffeeDecorator: | ||
| def __init__(self, coffee): | ||
| self._coffee = coffee | ||
|
|
||
| def cost(self): | ||
| self._coffee.cost() | ||
|
|
||
| class MilkDecorator(CoffeeDecorator): | ||
| def cost(self): | ||
| self._coffee.cost() | ||
| print("Adding milk: $1") | ||
|
|
||
| # Usage | ||
| coffee = Coffee() | ||
| decorated_coffee = MilkDecorator(coffee) | ||
| decorated_coffee.cost() | ||
| ``` | ||
|
|
||
| \note{ | ||
| In Python, decorators can be more flexible. The `MilkDecorator` is a class-based decorator that modifies the behavior of the `cost()` method of the `Coffee` class, just as in C++, but with simpler syntax. | ||
| } | ||
|
|
||
| # Comparison & Benefits | ||
|
|
||
| | Feature | C++ | Python | | ||
| |-----------------------|---------------------------------------------|------------------------------------------| | ||
| | Syntax | Classes, inheritance, virtual methods | Functions, classes, dynamic method calls | | ||
| | Flexibility | Requires careful memory management | More dynamic and flexible | | ||
| | Usage Scenarios | Performance-sensitive environments | More dynamic, quick prototyping | | ||
|
|
||
| \note{ | ||
| Summarize the differences in the implementation of decorators in C++ and Python, emphasizing the trade-offs between the two languages in terms of flexibility and performance. | ||
| } | ||
|
|
||
| # Code Walkthrough: C++ | ||
|
|
||
| - Step-by-step breakdown of C++ code: | ||
| - **Coffee**: Base class with the `cost()` method. | ||
| - **CoffeeDecorator**: A wrapper for extending functionality. | ||
| - **MilkDecorator**: Adds the milk cost dynamically. | ||
|
|
||
| \note{ | ||
| Take the time to walk through the C++ code. Discuss how decorators encapsulate functionality without modifying the original class and how they can be stacked for layered behaviors. | ||
| } | ||
|
|
||
| # Code Walkthrough: Python | ||
|
|
||
| - Step-by-step breakdown of Python code: | ||
| - **Coffee**: Basic coffee class. | ||
| - **CoffeeDecorator**: The decorator base class. | ||
| - **MilkDecorator**: Adds milk to the cost dynamically. | ||
|
|
||
| \note{ | ||
| Explain how Python decorators are simpler to implement and why this is a great tool for rapid development and cleaner code. | ||
| } | ||
|
|
||
| # Q&A and Wrap-up | ||
|
|
||
| - When would you prefer using decorators over other design patterns (like strategy or composite)? | ||
| - What are some potential drawbacks to using decorators excessively? | ||
|
|
||
| \note{ | ||
| Encourage the students to reflect on practical uses and drawbacks. Ask them to think about when they might reach for this pattern in their own projects. | ||
| } | ||
|
|
||
| # When to Use Each | ||
|
|
||
| | Technique | Use When... | | ||
| |------------------|-------------------------------------------| | ||
| | Sprout Method | Small change fits in one method | | ||
| | Sprout Class | Larger change needs state/context | | ||
| | Wrap Method | Add logic before/after a method safely | | ||
| | Wrap Class | Insert logic across many methods or state | | ||
|
|
||
| \note{ | ||
| You can draw a 2x2: *Sprout vs Wrap* on one axis, *Method vs Class* on the other. | ||
| Helps learners visualize choices. | ||
| } | ||
|
|
||
| # Summary | ||
|
|
||
| - **Sprout** = add new code that old code calls | ||
| - **Wrap** = write code that calls into the old code | ||
| - Use *Method* for simple logic, *Class* for complex or stateful logic | ||
| - These give you *safe entry points* into legacy code | ||
|
|
||
| # Final Thought | ||
|
|
||
| > "You don't need to clean up the whole kitchen to make a cup of tea. Just clear a spot." | ||
| > | ||
| > — Inspired by Feathers’ philosophy | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.