9.2.4, 9.2.5 and 9.2.6 - Refactoring - Extract Class, Renaming and Others - podcast episode cover

9.2.4, 9.2.5 and 9.2.6 - Refactoring - Extract Class, Renaming and Others

Nov 15, 202511 min
--:--
--:--
Download Metacast podcast app
Listen to this episode in Metacast mobile app
Don't just listen to podcasts. Learn from them with transcripts, summaries, and chapters for every episode. Skim, search, and bookmark insights. Learn more

Episode description

Software Engineering: A Modern Approach - Chapter 9 - Sections 9.2.4, 9.2.5 ,and 9.2.6 - Refactoring - Extract Class, Renaming and Others (AI-generated summary). Online book available at softengbook.org

Transcript

OK, let's dive straight in. We're picking U with a really foundational refactoring strategy. Extract class. This isn't just tightening U right? It's about really clarifying responsibilities when a class starts getting, well, bloated. Exactly. Bloated is a good word for it. You see a class accumulating way too many attributes, maybe handling too many different jobs. That's the signal. It often points to a violation of the the Single Responsibility principle. Right. One class, one job.

Pretty much. So if you spot attributes or responsibilities within that class that kind of feel like they belong together, like they form their own little group, extract classes. OK, pull that group out. Create a new class for them. And the original class then just holds an instance of that new class. Precisely. It gets an attribute of this new type you just created. It delegates the work. It cleans up the boundaries, makes things much more logical.

The book uses a good example, the Person class holding all the phone details directly like area code, number, alternative area code all stuffed into Person. Yeah, that's a classic. Initially it might seem OK, maybe convenient even. But pretty quickly that person class starts knowing way too much about phones. It's less focused. So the fix is. You create a new Phone class, it holds the area code and the number.

Then back in the Person class, instead of all those individual phone fields, you just have say a home Phone object and maybe a work phone object. Both are of type Phone. Oh OK, so person doesn't care how the phone number is structured anymore. Exactly. It just knows it has phones. It improves modularity massively, the codes intent is way clearer and responsibilities are split up properly. Makes reasoning about each part

much easier. And you know this idea connects to extract interface to similar principal different mechanism. Like with link list and array list. Perfect example. They're both list right, but implemented differently. So you extract a common list interface. This defines the shared behaviors, adding items, removing them, getting the size,

stuff like that. And the benefit for, you know, for us coding against it. The big win is you start following that prefer interfaces to concrete classes idea. If your code uses the List interface, you can easily swap an array list for a link list or something else entirely. And your code doesn't break. It doesn't care about the specific implementation, just the list contract.

That's huge for flexibility. So looking at your own code, this kind of thinking, breaking things down, defining clear interfaces is really crucial. It stops those huge God like classes from forming, makes things easier to grasp, easier to test, and yeah, easier to reuse later on. Helps you move faster in the long run. OK, moving on from structure to meaning, let's talk about renaming. Naming things the famous hard problem in computer science, right?

Besides cash and validation, of course. Yeah, the quote holds up. Naming is deceptively difficult. Why is it so hard and why do we constantly find ourselves renaming things? Well, there are usually a couple of reasons. Sometimes, honestly, the first name just wasn't very good. Not clear, not descriptive enough. We've all written temp or data one. Or maybe more often in systems that live a long time, the thing itself changes, its purpose

evolves. A method initially called Calculate tax might later also handle applying discounts, so the name becomes misleading. And a misleading name is just asking for trouble, misinterpretations, bugs. Exactly. Good names are vital for comprehension. They reduce that cognitive load when someone else, or even you six months later reads the code. But the tricky part isn't always finding the better name. It's updating everywhere the old name was used, right?

That sounds risky it. Can be definitely. Especially in a big system where a method or class is used all over the place. Miss one reference and boom, runtime error. It's tedious and error prone. How do we do it more safely? The book suggests safer approach using deprecation. Let's say you want to rename method F to G First you create the new method G You copy the original code from F into G Then you go back to the old method F

You mark it with at deprecated. That's a special annotation, OK. What does at deprecated actually do? It's a signal primarily to the compiler. In your IDE it says, hey, this thing is outdated, you probably shouldn't use it anymore. Compilers will usually issue warnings if someone tries to call the deprecated method F. So it warns developers and what happens inside F now?

Inside the deprecated method F you just put a single line, a call to the new method G So F now just delegates to G. Got it. So old code calling F still works, but developers get warned and the actual logic lives in G. Exactly. It's like putting up a detour sign. The old road still gets you there for now, but everyone's encouraged to use the new highway. That makes so much sense. It doesn't force everyone to update instantly, gives teams time to adapt. Much safer.

Yeah, it embraces that idea of making changes in small, safe steps, baby steps, avoids breaking things. It really reflects a core principle for managing large software projects. Introduce change gradually manage the risk, allow for transition. Makes sense. Stability is key. Absolutely. Especially when you have complex dependencies, you minimize disruption. Then later, once everyone's migrated to G, you can safely delete F. OK, great strategy.

Now let's shift gears slightly. We've talked big structure changes like extract glass and crucial clarity changes like renaming. What about those smaller, more localized improvements? Things you might do just inside a single method. Yeah, there are several really useful ones for that fine green cleanup. They might seem small, but they add up significantly for readability and maintainability. Like what? Well, first up is extract variable.

This is all about simplifying complex expressions. Imagine you have a long calculation, maybe part of a formula, all crammed into one line. It can be hard to read, hard to see the structure. Right, like nested function calls or complex math. Exactly. So extract variable says, take a meaningful chunk of that complex expression, calculate it separately and assign the result to a new variable with a good descriptive name. So you break it down, give names

to the intermediate steps. Precisely. The book might show something like pulling out the BB fourac ache part of the quadratic formula into a variable called delta or discriminate. The original formula then becomes shorter, uses delta, and is immediately easier to understand. Reduces the mental juggling. OK, that's clear. Makes sense. What else? Remove flag. This one targets those boolean flag variables we sometimes use to control loops or signal results within a method.

Like setting found in true inside a loop when you find what you're looking for. That's the classic example. You loop through something, maybe searching an array. If you find the item you set, found a true and then maybe break. After the loop, you check if found. The refactoring suggests getting rid of that found variable altogether by using control flow statements directly. As soon as you find the item inside the loop, just return true right there.

If the loop finishes without finding it, you return false after the loop. OK, direct exit. Yeah, it eliminates the need for the flag variable less state to track cleaner flow. Often shorter code makes the exit condition more explicit. Nice. I like that fewer variables is usually good. Definitely. Then there's a really powerful one. Replace conditional with polymorphism. Oh, OK, polymorphism.

This sounds more involved. It is, but it's fantastic for cleaning up complex switch statements or long if else if else chains, especially when the condition is based on the type of an object. Like checking if student dot type equals undergrad then else if student dot type master.

Exactly that kind of scenario. The book uses an example like calculating a student's research grant based on their type undergrad, master, PhD. Instead of that big switch or if else block in the code that uses the Student object, the refactored code becomes incredibly simple. Maybe just double grant student dot get grant? Whoa, OK, how does that work? Where did the logic go? It leverages object oriented programming. You make get grant an abstract method in a base student class.

Then each specific subclass undergraduate student, master student, PhD student provides its own implementation of GET grant. So the logic for the grant amount lives inside each student types class. Precisely when you call student dot get grant polymorphism ensures the correct version of the method for that specific Student object gets executed. It elegantly distributes the responsibility. That's cool.

It gets rid of the big conditional and makes it easy to add new student types later without modifying the original calling code. Exactly. Much more extensible, adheres better to the open closed principle. And finally a really important one, especially in older code bases. Remove dead code. Just. Deleting code. Just deleting code that isn't used anymore, methods that are never called classes, never instantiated variables or attributes, never read stuff

that's just sitting there. That sounds simple, maybe even obvious. But why is it so important, especially in big old projects? It's not hurting anything if it's not called, is it? But it is hurting things indirectly in systems maintained over years by many different developers. Dead code just accumulates. It's inevitable, and it causes real problems. It's clutter. It makes the code base bigger and harder to navigate. New developers waste time trying to understand code that does nothing.

They might be afraid to remove it, thinking it's used somewhere they miss. Right, that fear factor. What if this breaks something obscure? Exactly. It adds confusion and slows down comprehension. Removing it makes the code base leaner, cleaner, easier to understand, and crucially, easier and safer to change going forward. You eliminate the risk of someone acts as evidently resurrecting old buggy logic. It's essential maintenance.

OK, that makes total sense. It's not just tidiness, it's risk reduction and maintainability. So we've covered some really key refactoring techniques today. Extract class for better structure. Renaming using deprecation for safer transitions. And those valuable local improvements like extract variable, remove flag using polymorphism over conditional. And definitely remove dead code. Keep that code base clean. It really paints a picture of refactoring as this ongoing

process, not a one runoff event. Absolutely. It's about continuous design improvement, making sure that as the software grows and changes, the underlying structure stays healthy, adaptable and maintainable. It's an investment really in the future health of the project. Thank you for joining us for this deep dive.

Transcript source: Provided by creator in RSS feed: download file
For the best experience, listen in Metacast app for iOS or Android