Welcome back to the Deep dive. Today we're not just talking
about code cleanup in general. We're doing a really focused
exploration, a surgical strike you might say, into two core
refactoring techniques in line method and move methyl.
That's right. And we're using Marco Tulio
Valente's Software Engineering a Modern approach as our guide.
It's all about understanding how these, well, seemingly small
adjustments are actually fundamental to building robust,
healthy software. Absolutely.
So refactoring just a level set quickly.
It's about improving the inside without changing the outside.
Precisely improving the design, making it cleaner, more
maintainable, easier to work with down the line, all without
altering what the software actually does for the user.
And our mission today specifically is to unpack inline
method and move method. Why do they matter so much?
OK, let's stop straight in then with inline method.
Now many developers I think are familiar with extract method
pulling code out into a new function.
Inline method is well, the opposite journey isn't it?
Putting code back. Exactly.
It's about taking that separate methods code and folding it
directly into wherever it was called from.
So. Why would you do that?
It feels a bit counterintuitive, like you're undoing organization
when is less actually more here. That's the key question, and
Valente points to very specific situations.
Primarily it's beneficial when a methods body is incredibly
small, like maybe just one or two lines, sometimes even just a
single expression. OK, really tiny.
Yes, tiny. And crucially, it's also when
that method isn't called from many different places, maybe
just once or perhaps a couple of times in very close proximity.
So it's not providing much reuse benefit anyway.
Exactly. In those cases, the typical
advantages of a separate method, you know, abstraction,
modularity. They're minimal.
Sometimes such a tiny, rarely used method can even add
unnecessary complexity, a little bit of indirection that just
makes you jump around the code for no real game.
Like cognitive overhead, having to keep track of too many tiny
pieces. Precisely.
It can fragment the logic. You're trying to follow a
process and suddenly you have to navigate a way to understand
this one tiny step before coming back.
Inline method helps consolidate that makes the main flow
clearer. So the practical step is you
find these tiny, maybe single use methods, you remove the
method definition itself and you just paste it's, well, one or
two lines of code directly into the spot or spots where it was
being called the call sites. That's the essence of it,
cleaning up that sort of micro clutter or maybe premature
abstraction. Makes the code feel more direct.
Yes, but it's worth noting, as Valente highlights, inline
method is generally less common, maybe less impactful overall
than its counterpart extract method.
Which makes sense. Usually the drive is to
breakdown complexity, not consolidate simplicity, right?
But it's still a valuable tool for tidying up when abstraction
maybe went a bit too far or wasn't really needed in the 1st
place. The.
Source had a good clear example, didn't it?
Something about writing to a file?
It did a method called write content to file sounds
important, but its entire body was just a single line of code.
Just one line. Just one, and critically, it was
only ever called from one other place, a method called write.
So 0 reuse minimal abstraction value.
Pretty much. So the developers decided OK,
this extra method isn't pulling its weight.
They removed right content to file and just put that single
line of code directly into the right method.
And the outcome? Simpler code, less indirection,
easier to read the right methods, logic straight through,
and crucially, no change at all to the external behavior.
OK, so that's inline method neatening things up by removing
very small underused functions. But sometimes the issue isn't
that a function is too small, but that it's, well, in the
wrong house entirely. And that brings us to move
method. This one feels like it has
bigger architectural implication.
Oh definitely. Move method is often a much more
significant refactoring. It's about getting functionality
to reside in the most logical place.
So what's the core problem at solving?
Why would a method end up in the wrong class to begin with?
It happens often during development or evolution.
You might find a method sitting in, let's call it Class A, but
when you look closely, that method uses features, data,
other methods from Class B much more heavily than it uses
anything from its own Class A. So it's real center of gravity
is somewhere else. Exactly.
It's more interested in Class B's business than Class A's.
It feels misplaced, like keeping your gardening tools in the
bedroom closet. They work, but it's not where
they belong. Right.
And moving them has real benefits.
Huge benefits. According to the source, moving
the method to Class B where it belongs does 2 critical things.
First, it significantly improves the cohesion of Class A Class A
becomes more focused on its core responsibility because unrelated
logic has been moved out. Makes sense?
Tighter focus. Second, it reduces the coupling
between Class A and Class B. Because Class A no longer needs
to host this method that heavily depends on B, their
interdependence decreases. Less tangled up, which means
changes in B are less likely to break A and vice versa.
Precisely. It makes both classes easier to
understand, easier to modify independently, and easier to
test. That's vital for long term
maintainability. So this sounds like it really
impacts the overall system modularity.
Getting methods into the right classes strengthens those
boundaries. It absolutely does.
It's a key technique for improving the architectural
health of a system, ensuring responsibilities are clearly
delineated. Valente gives an example.
I think it was average among medians.
Yes, that was it. Right, so this method average
among medians was initially found in a class called platform
test util a utility class. Sounds generic, but the method
itself calculated the average of medians in an array, A
mathematical array focused operation, and crucially, it had
basically no connection to anything else in platform test
util didn't use its data, didn't relate to its other utility
functions, it was just there. Kind of an island within the
class. Exactly.
It felt like it belonged more with general array operations,
so the developers moved it. Where did it go?
They moved it to a different class, potentially a new one
called Array util, whose entire purpose was handling array
manipulations. Makes perfect sense putting it
with its peers, functionally speaking.
Precisely. It's now in a class where its
purpose aligns with the classes overall responsibility.
Yeah, better cohesion, better findability.
OK, but moving a method, especially in a big system,
raises A practical issue. What about all the code that was
calling the method in its old location?
You move the method, say from Class A to Class B.
Don't you then have to find and update every single call site?
That sounds risky. It can be, yes.
The ripple effect is a real concern, but there are standard
ways to manage this. If the method was static, it's
often simpler. Usually just need to change the
class name prefix at each call site so A method becomes B
method. Compilers help find these.
OK, static is manageable. What about non static methods or
cases where the calling code doesn't already have a reference
to an object of Class B? That's where it gets more
complex, but there's a very clever common technique
mentioned in the source, the forwarding method.
Forwarding method, yeah. Instead of immediately deleting
the method from the original class, you leave behind a simple
version. This version in Class A doesn't
do the work anymore. Its only job is to immediately
call the method in its new location Class B.
Like a redirect? Exactly like a redirect.
So if a client was calling AF and you move F to Class B, you
can leave a method F and A that just does return BF, assuming A
has a reference to B. So the client code doesn't even
need to know the method moved, at least not initially.
Correct. It allows you to make the move
internally and then update the client calls gradually, or
perhaps only when those clients are modified for other reasons.
It decouples the refactoring from the immediate need to
update all callers. Much safer in large systems.
That's a really pragmatic approach.
Makes a big change much less disruptive.
Now, you mentioned moving between classes.
Are there special kinds of moves within a class hierarchy, like
between parent and child classes?
Yes, absolutely. Filente covers these two.
When move method happens within an inheritance structure, we
often use more specific names. There's pull up method.
This is when you move a method from a subclass up into its
superclass. OK, why would you do that?
Usually because the same method exists duplicated in several
sibling subclasses. By pulling it up into the common
parent class you eliminate that redundancy.
You have 1 shared implementation instead of many copies.
Promotes reuse, simplifies maintenance.
Consolidating shared behavior upwards makes sense.
What's the opposite? That would be pushed down
method, moving a method from a superclass down into one or more
of its subclasses. And the reason for pushing down?
This is typically done when a method in the superclass is
actually only relevant or used by a specific subclass, or maybe
just a few. Of them.
So it doesn't really belong to all children, right?
Pushing it down to only the relevant subclasses makes a
superclass cleaner and more general, and clarifies the
behavior is specific to those children.
It avoids burdening other subclasses with irrelevant
methods. So pull up centralizes
commonality, push down localizes specificity, both about getting
behavior to the right level in the hierarchy.
Exactly. They're crucial for maintaining
clean, understandable inheritance relationships.
It's really interesting how these refactorings aren't always
isolated actions. You mentioned they can be
combined performed in sequence. Yes, very much so.
Refactoring is often an iterative process combining
techniques to achieve a larger goal.
The source gives a nice little example of this.
How does that work? OK, imagine you start with a
method F in Class A, and inside F there are two chunks of work.
Let's call them statement S1 followed by statement S2.
OK, F does S1 then S2. Now you might look at S2 and
think that's a distinct piece of logic.
So your first step could be an extract method.
You pull S2 out into its own new method, say G still within Class
A. So now F just does S1 and then
calls this G. Exactly.
But then you look at this new method G and you realize based
on what it does, maybe it's actually more related to Class B
uses BS data or services. So the extraction revealed a
misplaced responsibility. Precisely.
So the next step is a move method.
You move method G from Class A over to Class B OK, so the final
state is Method F in Class A still does S1, but now instead
of calling this dot G, it calls BG, invoking the method on an
instance of Class B. Wow.
So extract followed by move. Two smaller steps to achieve a
significant structural change, Yes.
It shows how you can use these refactorings like building
blocks. You don't have to refactor the
whole world at once. Small, deliberate steps, often
combining techniques, lead to much better, more maintainable
designs overtime. That really highlights the craft
involved. It's not just banging out code,
it's thoughtfully shaping it, making it easier to understand,
maintain and evolve later on. So wrapping up our deep dive
today, we've seen inline method as a way to clean up tiny,
underused functions. And move method is a really
powerful technique for getting functionality into the right
class, boosting cohesion and cutting down problematic
dependencies. And it's crucial to remember
these aren't just cosmetic changes.
Refactoring like this is fundamental to managing
technical debt and ensuring software can adapt to future
needs. It's about building systems that
last. Thank you for joining us on this
deep dive.