=============30/07/2020=============

Today I started reading the book recommended at CompSci701 class:
Martin Fowler (feat. Kent Beck) Refactoring: Improving the Design of Existing Code. Addison-Wesley (2012).

I am reading the Chinese translated version, translated by Mr 熊节. I will write my note back in English to reinforce learning.

Introduction

When to Refactor

This is a nice description for when to refactor, which could help me consider how I improve my assignment.

If you want to add a new feature to the program, and the structure of the code makes it inconvenient to do so, then refactor before adding the feature.

Before refactoring

Good tests are the foundation for refactoring!

Refactoring could introduce bugs, so write a reliable testing environment to ensure you don’t break things during refactoring.

Extract methods

The smaller your code block, the easier to manage, change and move them.
To extract methods, you need to find the local variables and parameters.
Any unchanged variables in the long block could be passed to the extracted method/function as arguments.
Be careful with changed variables”. If there is only one variable’s value being changed, it can be the return value of the new argument.

Placing methods in the right place

Most of the time, functions/methods should be put in where the data it uses belongs. This is relatable to a static method I wrote for Kalah:

1
2
3
4
5
6
7
8
9
10
11
/**
* Generate a pair of player partner for Kalah game.
* */
public static KalahPlayer[] createNewPlayerPair() {
KalahPlayer[] newPlayerPair = new KalahPlayer[2];
newPlayerPair[0] = new KalahPlayer(DEFAULT_PLAYER_ONE_NAME);
newPlayerPair[1] = new KalahPlayer(DEFAULT_PLAYER_TWO_NAME);
newPlayerPair[0].setOpponent(newPlayerPair[1]);
newPlayerPair[1].setOpponent(newPlayerPair[0]);
return newPlayerPair;
}

To start with it was in the Kalah class because this is where it is used.
Then I moved it to KalahJudge class because I don’t want Kalah to know too much about processes other than driving the game state.
Finally I moved it to KalahPlayer, because:

  • KalahPlayer is all this method deals with and needs to know;
  • If I put this “constructor” method in KalahPlayer, I will be able to make KalahPlayer‘s constructor private, because this should be the only way to create a valid KalahPlayer instance.

Try to get rid of temporary variables

Temporary variables could be problems. They are only valid in the function they belong to, and therefore could contribute to a long and complex function. It is easy to lose track of temporary variables in a long function.

Performance issue in refactoring

Replacing temporary variables with query could cause performance issue. For example, there might need to be extra loops in the query method. It says could because you never know until you run assessments on the running time. You do not need to worry about this when you refactor. Worry about them when you are optimising - by that time you are at a good position with well-refactored code.

Don’t switch on another object’s attribute

With the Movie and Rental example in the book, getCharges switch among different movie types.

This method is originally in the Rental class and uses the getDaysRented() method belonging to Rental and the getPriceCode() method belonging to Movie:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
double getCharge()
{ double result=0;
switch(getMovie().getPriceCode()){
caseMovie.REGULAR:
result+=2;
if(getDaysRented()>2)
result+=(getDaysRented()-2)*1.5;
break;
caseMovie.NEW_RELEASE:
result+=getDaysRented()*3;
break;
caseMovie.CHILDRENS:
result+=1.5;
if(getDaysRented()>3)
result+=(getDaysRented()-3)*1.5;
break;
}
}

It should be moved to Movie class and pass the result of getDaysRented as an argument.
This is because this method depends on how many possible priceCode there are in Movie.
If the potential change to this system is an addition of new movie type/priceCode, we want to contain the change caused by adding new movie types within the Movie class.

State pattern by Gang of Four

This looks like the Strategy pattern, but is different because Price class represents a “state” of the movie, instead of a “behaviour”. It could be updated from NEW to REGULAR after some time.

Related refactoring technique:

  • Replace Type Code with State/Strategy
  • Replace Conditional With Polymorphism

Interactions after adding the State pattern:

UML after adding the State pattern:

Principles of Refactoring

What is refactoring

A change to the internal structure of a software to improve its comprehensibility and reduce its maintaining cost, without changing its observable behaviours.

When to refactor

You do not refactor for the sake of refactoring. You have another goal and use refactoring to facilitate that.

Three strikes and refactor

Don Roberts’ “The rule of three”:

The third time you do something similar, you refactor.

Refactor when adding new functionality

  • Refactoring helps us understand the code to be modified;
  • Refactoring makes the structure clear and easier to be extended.

Changing Published Interfaces when Refactoring

If you change the interface, anything could happen.

The problem with refactoring is that some refactoring techniques DO change interfaces - as simple as Rename Method (273). What’s its impact on the treasured notion of Encapsulation?

  • If all callers are under your control, no problem - even with a public interface.
  • Published is a step ahead public. If an interface is published, i.e.

Maintain both old and new interfaces

Rewrite old function to call the new one

Don’t copy the function implementation, or you’ll get stuck into code duplication!

Use Java’s deprecation facility

Mark the old interface as deprecated so that your callers will notice.

Try not to publish interfaces unless necessary

Organizations with an overly strong notion of code ownership tend to publish interfaces inside the team too early. This will introduce unnecessary needs to maintain new/old interfaces.
Change your notion of ownership, and let everyone be able to change others’ code to adapt to new interfaces. This will make refactoring much smoother.

A special interface change: adding a new exception in throws clause

This is not change to function signature, so you cannot use delegation to cover it. But the compiler will be unhappy with this change.

To solve this, define a superclass exception for a whole package (such as SQLException for java.sql) and ensure that public methods only declare this exception in their throws clause.

This way, callers will only know the more generic exception, while we could define new subclass exceptions as we need.

When not to refactor

When rewriting is just easier

A clear signal for rewriting instead of refactoring is: the current code could not function properly.

Remember, before refactoring, your code should be functional in most cases.

Break into components first

A compromise route is to refactor a large piece of software into components with strong encapsulation. Then you can make a refactor-versus-rebuild decision for one component at a time.

Before the deadline of the project

Avoid refactoring at this time, because the productivity you gain will only be available after the deadline.

Refactoring and Performance

Even if you totally know the system, do measure its performance instead of assuming.

The secret to fast software, in all but hard real-time contexts, is to write tunable software
first
and then to tune it for sufficient speed.

Three ways to write fast software:

Time budgeting

Often used in hard real-time system.

Budget each component certain amount of resource - time and footprint.

Constant attention approach

Try to keep high performance anytime doing anything.
This feels attractive but are not so effective. Problems:

  • Changes that improve performance usually make the program harder to work with. This slows development.
  • The performance improvements are spread all around the program, and each improvement is made with a narrow perspective of the program’s behavior.

Delay until your performance optimization stage

If you optimize all the code equally, you end up with 90 percent of the optimizations wasted, because you are optimizing code that isn’t run much.
Focus on writing well-mannered program, and then follow specific procedure to adjust the system’s performance later in your development, when you enter a performance optimisation stage.

Use a profiler to monitor the system

First you use a profiler to tell you what places in your system consumes the most time and space.

Focus on “performance hot spots” for optimisation

Then you could focus on these “performance hot spots” to conduct optimizations.

Stay cautious

Just like refactoring, you should adjust in small steps when optimising:

  • Adjust in small steps

  • For each step, compile, test, rerun the profiler. If no improvements, undo the adjustment.

    Follow the above process of finding and removing hot spots, until you get the performance wanted by your client.

How refactoring helps late optimisation

Although refactoring could slow down the software in the short term, a well-refactored programme helps this style of refactoring in two ways:

  • More time to focus on performance because adding new features is now quicker.
  • Finer granularity for your performance analysis.

=============31/07/2020: Bad Smells=============

Today I’ll be reading chapter 3: Code smells. The number behind the technique name is the page number in the book in the Chinese Kindle version for future reference.

No set of metrics rivals informed human intuition.

Duplicated Code

  • Same expressions in two functions in one class: Extract Method(110)
  • Same expressions in sibling subclasses: Extract Method(110) and Pull Up Method(332) into their superclass
  • Similar methods in sibling subclasses: Extract Method(110) to separate identical parts and differences, then you mind find the opportunity to use Form Template Method(345) to get a Template Method pattern.
  • Functions doing the same thing with different algorithms: choose the clearer one and use Substitute Algorithm (139).
  • Two unrelated classes using duplicated code:
    • Extract Class(149) and move the duplicate code into an independent class
    • Or maybe it should only belong to one class and be invoked in the other.

Long Method

The object programs that live best and longest are those with short methods.
All of the payoffs of indirection— explanation, sharing, and choosing—are supported by little methods

Older languages carried an overhead in subroutine calls, which deterred people from small methods. Modern OO languages has almost eliminated this overhead.
It takes effort to read the methods though, so the key to making small methods comprehensible is a good name, so that your reader do not need to jump to the methods to know what it does.

Be more aggressive about decomposing methods

A heuristic we follow:

When you feel the need to add comment to explain something, extract what needs to be explained into a new method, and name it with its intention(not how it does the job).

We can do this on even a short line of code, even if the method is longer than the code it replaces.

The key here is not method length, but the semantic distance between what the method does and how it does it.

When there are many parameters and temporary variables

If you use Extract Method(110), you’ll get a lot of parameters passed to the extracted method.

  • Replace Temp with Query(120) to eliminate these temporary elements.
  • Introduce Parameter Object(295) and Preserve Whole Object(288) to make your long parameter list cleaner.
  • If you’ve tried that, and you still have too many temps and parameters, it’s time to get out the
    heavy artillery: Replace Method with Method Object(135).

Deciding which code to extract

Comments

Comments often signal the semantic distance between the code’s intention and how it does it.
A block of code with a comment that tells you what it is doing can be replaced by a method whose name is based on the comment.

Conditionals and loops

Conditionals and loops also give signs for extractions.

  • Use Decompose Conditional(238) to deal with conditional expressions.
  • With loops, extract the loop and the code within the loop into its own method.

Large Class

If you want to do too many things with a single class, it often shows up as too many instance variables - then duplicated code will not be far behind.

  • When you have too many instance variables:
    • Extract Class(149) and organise several variables into a new class.
    • If several variables has the same prefixes or suffixes they might belong to the same component
    • You could Extract Subclass(330) when suitable.
  • When you have too much code: Also Extract Class(149) and Extract Subclass(330)
    • First find out how the clients use the code, then Extract Interface(341)
  • If your large class is a GUI class, you could move data and behavior to a separate domain object.
    • This may require keeping some duplicate data in both places and keeping the data in sync. Duplicate Observed Data(189) suggests how to do this.

Long Parameter List

In object-oriented programs, parameter lists tend to be much smaller than in traditional programs. This is because you just need to pass your method with enough objects for it to help itself to everything it needs.

  • If a request to an existing object could replace a parameter, Replace Parameter with Method(292). Here the “existing object” could be:
    • a field within the class that the method belongs
    • another parameter
  • Preserve Whole Object(288) to collect a handful of data from the same object, and replace these parameters with that object.
  • If some data items have no logical object, you could Introduce Parameter Object(295) to create a “parameter object”.
  • One important exception: Sometimes you do not want to create dependency from the called object to the larger object. In those cases unpacking data and sending it along as parameters is reasonable.
    • pay attention to the pain involved. If the parameter list is too long or changes too often, you need to rethink your dependency structure.

Divergent Change

Divergent change occurs when one class is commonly changed in different ways for different reasons.

To clean this up you identify everything that changes for a particular cause and use Extract Class to put them all together.

Shotgun Surgery

You smell Shotgun Surgery when every time you make a kind of change, you have to make a lot of little changes to a lot of different classes.

  • In this case you want to use Move Method and Move Field to put all the changes into a single class.
  • If no current class looks like a good candidate, create one.
  • Often you can use Inline Class to bring a whole bunch of behavior together.

Divergent change is one class that suffers many kinds of changes, and shotgun surgery is one change that alters many classes.
Either way you want to arrange things so that, ideally, there is a one-to-one link between common changes and classes.

Feature Envy

  • A classic smell: a method’s interest is higher than its interest in its own class. Often desiring for data.
  • Solution: Move the method to the place it wants to be.
  • When a method wants to use features from multiple classes, decide by seeing which class owns the data most used by this method.

Some Design Patterns require you to break this rule

These patterns are used to fight against the smell of Divergent Change, and to put things that change together in the same place.

GoF’s Strategy pattern

Makes it easy for you to change a method’s behaviour, because it isolates a small amount of behaviours that need to be overridden.
The strategy object’s methods will want data from its owner.

GoF’s Visitor pattern

The visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures.
The visitor object’s methods will want data from the visited element.

Data Clumps

Data items that always go to places together should have their own object.

  • Extract Class(149) to extract these data items into the same object.
  • Move your attention to the method signature:
    • Introduce Parameter Object(295)
    • Preserve Whole Object(288)
      This way, you could shorten a lot of parameter lists and simplify method calls.
      Don’t worry if only parts of the new object are used, as long as the new object replaces two or more items, it’s worth the change.

A good way of judgement: If replace one item in the clump, will other data items lose their meaning? If so, it is a clear signal: they should be in an object.

Primitive Obsession

  • Replace Data Value with Object(175) to replace the standalone values with objects.
  • Replace Type Code with Class(218)
  • Replace Type Code with Subclass(213), e.g. replace Movie.priceCode with subclasses of Price: NewPrice, RegularPrice, ChildrenPrice
  • Replace Type Code with State/Strategy(227), e.g. use price.calculateCharge(daysRented) to calculate Rental charge for a movie. price maintains a state for the movie.
  • If you find yourself picking data from an array, try Replace Array with Object(186)

Switch Statement

A very significant symptoms of object-oriented code: the lack of switch statement.
The essential problem to statement is duplication.
You often find the same switch cases appearing in multiple places, and if you need to add a new case, you need to go find all its appearances.

The OO notion of polymorphism gives you an elegant way to deal with this problem.

Where should the polymorphism go?

Switch clause often switches among type code. You want “method or class for the type code”, so you should:

  • Use Extract Method(110) to extract the switch clause to a method
  • Use Move Method(142) to move this method to the class being switched (maintaining the type code/to be made polymorphism)
  • Use Replace Type Code with Subclasses(223) or Replace Type Code with State/Strategy(227) in this class.
  • Once the logic of switching is migrated to this class, use Replace Conditions with Polymorphism(255).

If it’s just some options within a single method

If you only want to some simple choices, polymorphism is a bit overkill. You could consider:

  • Replace Parameter with Explicit Methods(285)
  • If one of the cases is null, you could try using Introduce Null Object(260).

Parallel Inheritance Hierarchies

In this scenario, whenever you add a subclass to one class, you’ll have to add a subclass to another class in parallel.
The strategy to eliminate this duplication:

  • Let one instances of one hierarchy refer to instances of the other.
  • Then you could choose to use Move Method(142) and Move Field(146) to get rid of the hierarchy on the referring class: now it contains a field (the referred instance) that takes care of the inheritance relationship.

Lazy Class

Each class costs time and effort to maintain. Kill classes that aren’t doing enough to pay for itself.

  • If some subclasses are not doing lots, try Collapse Hierachy(344)
  • For components almost useless outside one context, use Inline Class(154)

Speculative Generality

Don’t say “Oh one day we’ll need this”. See if you are using them now.

  • Collapse Hierarchy (344) if some abstract classes are not so useful.
  • Unnecessary delegation could be removed with Inline Class(154).
  • Methods with unused parameters should be subject to Remove Parameter (277).
  • Methods named with unnecessarily abstract names should be brought down to earth with Rename Method (273.
  • If the only user of a method/class are test cases, get rid of the test cases and the code.

Temporary field

Sometimes you see an object in which an instance variable is set only in certain circumstances.
Such code is difficult to understand, because you expect an object to need all of its variables.

  • Use Extract Class(149) to create a home for the poor orphan variables. Put all the code that concerns the variables into the component.
  • You may also be able to eliminate conditional code by using Introduce Null Object (260) to create an alternative component for when the variables aren’t valid.

A common case of temporary field occurs when a complicated algorithm needs several variables. Because the implementer didn’t want to pass around a huge parameter list (who does?), he put them in fields. But the fields are valid only during the algorithm.
In this case you can use Extract Class(149) with these variables and the methods that require them. The new object is a method object and knows how to do the algorithm.

I found an article by Refactoring Guru: Replace Method with Method Object explaining what is a method object:

  • Problem: You have a long method in which the local variables are so intertwined that you can’t apply Extract Method.
  • Solution: Transform the method into a separate class so that the local variables become fields of the class. Then you can split the method into several methods within the same class.

Message Chains

You see message chains when a client asks one object for another object, which the client then asks for yet another object, which the client then asks for yet another another object, and so on. You may see these as a long line of getThis methods, or as a sequence of temps.

Navigating this way means the client is coupled to the structure of the navigation.
Any change to the intermediate relationships causes the client to have to change.

  • Hide Delegate(157):

However, this way tends to make a handful of objects “Middle Man”.

  • A better option: first observe what the object got at the end of the chain is used for. See if you could use Extract Method(110) to extract the code that uses this final object into a new method, and then use Move Method(142) to push this method down the chain. If several clients of one of the objects in the chain want to navigate the rest of the way, add a method to do that.

Middle Man

You look at a class’s interface and find half the methods are delegating to this other class.

  • Use Remove Middle Man(160) and talk to the object that really knows what’s going on.
  • If only a few methods aren’t doing much, use Inline Method(117) to inline them into the caller.
  • If there is additional behavior, you can use Replace Delegation with Inheritance (355) to turn the middle man into a subclass of the real object. That allows you to extend behavior without chasing all that delegation (Your old middle man now knows how to do the delegated job himeself, and he also knows some extra stuff).

Inappropriate Intimacy

Sometimes classes become far too intimate and spend too much time delving in each others’private parts.

  • Use Move Method (142) and Move Field (146) to separate the pieces to reduce the intimacy.
  • See whether you can arrange a Change Bidirectional Association to Unidirectional(200).
  • If the classes do have common interests, use Extract Class (149) to put the commonality in a safe place that the two lovers could share interests.
  • Use Hide Delegate (157) to let another class act as go-between.

Inheritance often can lead to overintimacy. Subclasses are always going to know more about their parents than their parents would like them to know. If it’s time to leave home, apply Replace Inheritance with Delegation (352).

Alternative Classes with Different Interfaces

  • Use Rename Method (273) on any methods that do the same thing but have different signatures for what they do.
  • Keep using Move Method (142) to move behavior to the classes until the protocols are the same.
  • If you have to redundantly move code to accomplish this, you may be able to use Extract Superclass (336).

Incomplete Library Class

  • If there are just a couple of methods that you wish the library class had, use Introduce Foreign Method (162).

  • If there is a whole load of extra behavior, you need Introduce Local Extension (164).

    Data Class

    These are classes that have fields, getting and setting methods for the fields, and nothing else.
    Such classes are dumb data holders and are almost certainly being manipulated in far too much detail by other classes.

  • In early stages these classes may have public fields. If so, you should immediately apply Encapsulate Field (206) before anyone notices.

  • If you have collection fields, check to see whether they are properly encapsulated and apply Encapsulate Collection (208) if not.

  • Use Remove Setting Method (300) on any field that should not be changed.

  • Look for where these getting and setting methods are used by other classes. Try to use Move Method to move behavior into the data class.

  • If you can’t move a whole method, use Extract Method (110) to create a method that can be moved.

  • After a while you can start using Hide Method (303) on the getters and setters.

Data classes are like children. They are okay as a starting point, but to participate as a grownup object, they need to take some responsibility.

Refused Bequest

  • Traditional thoughts: A subclass should want to play with all the methods and data from its superclass. If it does not, there are mistakes in the design of the inheritance hierarchy. The methods and data that the child do not want to play with should be sent to a sibling of the child through using Push Down Method (328) and Push down Field (329).

  • Martin & Beck’s opinion: If Refused Bequest causes confusion and problem, follow the traditional thoughts. Nine times out of ten this smell is too faint to be worth cleaning.

  • The smell of refused bequest is much stronger if the subclass is reusing behavior but does not want to support the interface of the superclass.

  • We don’t mind refusing implementations, but refusing interface gets us on our high horses. In this case, however, don’t fiddle with the hierarchy; you want to gut it by applying Replace Inheritance with Delegation(352).

Comments

Comments help us to identify bad smells. First you should eliminate the bad smells through your refactoring techniques. Often after refactoring, we realise that the code has told everything.

  • If you need comment to explain a block of code, try Extract Method (110)
  • If the method has been extracted, try Rename Method (273)
  • If you want to state some rules about the required state of the system, try Introduce Assertion (267).

When you feel the need to write a comment, first try to refactor the code so that any comment becomes superfluous.

A good time to use a comment is when you don’t know what to do. With comments you can:

  • Describe what is going on
  • Indicate areas in which you aren’t sure.
  • Say why you did something.

These comments will give information to future modifiers.

============= 1/8/2020 =============

Build Tests

Make sure all tests are fully automatic and that they check their own results.

Benefit of frequently running automatic test:

A suite of tests is a powerful bug detector that decapitates the time it takes to find bugs. (while the bugs are freshly introduced)

The standard Java idiom for testing is the testing main. The idea is that every class should have a main function that tests the class. It’s a reasonable convention (although not honored much), but it can become awkward. The problem is that such a convention makes it tricky to run many tests easily. Another approach is to build separate test classes that work in a framework to make testing easier.

JUnit Test

Junit framework uses the composite pattern that allows you to group tests into arbitrary layers of suites:

Run your tests frequently. Localize tests whenever you compile—every test at least every day.

Unit Test vs Functional Test

  • Unit tests are highly localized. Each test class works within a single package.
  • Functional tests are written to ensure the software as a whole works.
  • Functional tests typically treat the whole system as a black box as much as possible.

When functional testers, or users, find a bug in the software, at least two things are needed to fix
it:

  • start with addding a unit test that exposes the bug
  • change the production code to remove the bug

When I get a bug report, I begin by writing a unit test that causes the bug to surface.

  • I write more than one test if I need to narrow the scope of the bug, or if there may be related failures.
  • I use the unit tests to help pin down the bug and to ensure that a similar bug doesn’t get past my unit tests again.

For refactoring purposes, I count on the unit tests—the programmer’s friend.

Test Tips

The key is to test the areas that you are most worried about going wrong. That way you get the most benefit for your testing effort.

It is better to write and run incomplete tests than not to run complete tests.

Think of the boundary conditions under which things might go wrong and concentrate
your tests there.

Don’t forget to test that exceptions are raised when things are expected to go wrong.

It is better to spend a reasonable time to catch most bugs than to spend ages trying to catch them all.

============= 3/8/2020 =============

Now I start reading the Catalog of Refactorings.

Refactoring techniques on methods

Replace Method with Method Object

This is a technique that interests me a lot, as it helps break down long method that actively manipulate multiple local variables.

There are two ways to share variables between two methods: pass them as arguments/return values, and making them globally accessible to both to them.

Extracting a Method Object makes the old local variables accessible to all extracted short methods.

The role of temporary variables

Fowler refreshed my understanding of temporary variables. In his opinion, temporary variables should also serve the comprehensibility of the code. When possible, extracting method should be preferred over using temporary variables; queries should be preferred over in-line calculation of a temp. If the method is too hard to decompose and temporary variables are necessary, each temporary method should only serve ONE purpose and should never be reused for a new purpose. The name of the temporary variable, just like methods, should clearly state its intention.

============= 7/8/2020 =============

Buried in 711 assignment for three days and AC today!!!🎈🎉🎈🎉 Back to the book.

Moving Features Between Objects

You’ve probably heard that a class should be a crisp abstraction, handle a few clear
responsibilities, or some similar guideline. In practice, classes grow. You add some operations
here, a bit of data there. You add a responsibility to a class feeling that it’s not worth a separate
class, but as that responsibility grows and breeds, the class becomes too complicated. Soon your
class is as crisp as a microwaved duck.

Introduce Foreign Method

Introduce a foreign method when a server class you are using needs an additional method, but you can’t modify the class. To do so, create a method in the client class with an instance of the server class as its first argument.

Introduce Local Extension

Introduce Local Extension when a service class needs several additional methods, but you can’t modify the class. You also want to reuse the additional methods in multiple places - if it is one-off thing, just create a foreign method.

Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original.

============= 9/8/2020 =============

Organising Data

Self Encapsulate Field

Question:

Inside a class, do we access the fields directly through the field name, or do we also use getters and setters?

Martin’s Choice:

  • I’m usually happy to do what the rest of the team wants to do.
  • Left to myself, though, I like to use direct variable access as a first resort, until it gets in the way.

When do we need to self encapsulate:
The advantages of indirect variable access are that it allows a subclass to override
how to get that information with a method
and that it supports more flexibility in managing the data, such as lazy initialization, which initializes the value only when you need to use it.

Encapsulate Collection

Often a class contains a collection of instances. This collection might be an array, list, set, or vector. Such cases often have the usual getter and setter for the collection.

  • A getter for a multivalued attribute should return something that prevents manipulation of the collection and hides unnecessary
    details about its structure. The getter should not return the collection object itself, because:
    • It allows clients to manipulate the contents of the collection without the owning class’s knowing what is going on.
    • It reveals too much to clients about the object’s internal data structures.
  • There should not be a setter for collection: rather there should be operations to add and remove elements.
    • This gives the owning object control over adding and removing elements from the collection.
  • With this protocol:
    • the collection is properly encapsulated,
    • the coupling of the owning class to its clients is reduced

Replace Type Code with Subclasses

Building the stage for Replace Conditional with Polymorphism.

Replace Type Code with Strategy/State

This is similar to Replace Type Code with Subclasses, but can be used if the type code
changes during the life of the object or if another reason prevents subclassing. It uses either the
state or strategy pattern.

============= 10/8/2020 =============

Simplifying Conditional Expressions

Replace Nested Conditional with Guard Clauses

What is “Guard Clause“?

If the condition is an unusual condition, check the condition and return if the condition is true. This kind of check is often called a guard clause. (Beck)

Guard clauses either return from the method or throw an exception.

Introduce Null Object (null subclass)

The essence of polymorphism is that instead of asking an object what type it is and then invoking some behavior based on the answer, you just invoke the behavior.

Remember, null objects are always constant: nothing about them ever
changes. Accordingly, we implement them using the Singleton pattern
[Gang of Four]. Whenever you ask, for example, for a missing person,
you always get the single instance of that class.

Introduce Assertions

Assertions act as communication and debugging aids. In communication they help the reader understand the assumptions the code is making.
In debugging, assertions can help catch bugs closer to their origin. I’ve noticed the debugging help is less important when I write self-testing code, but I still appreciate the value of assertions in communciation. (Martin)

============= 12/8/2020 =============

Making Method Calls Simpler

Separate Query from Modifier

You get into trouble if you “modify” and “query” in the same methods.

A good rule to follow is to say that any method that returns a value should not have observable side effects.

Example in my code

I broke this role in the Kalah assignment with this method:

1
2
3
4
5
public int takeAllSeeds() {
int takeSeedNum = seedNum;
seedNum = 0;
return takeSeedNum;
}

It does two things:

  • query the current number of seeds
  • modify the seed number to 0.

In Martin’s opinion, this is bad. But how will breaking this method into two improve my programme, If every time seeds in a house needs to be modified, I will need the number of seeds right after? How is a getter then a remove call better than a call to takeAllSeeds?

Concurrency Issue

  • An important idiom in multi-thread system: Test then Set
  • You need to retain a third method for the query-and-modify operation.
  • The query-and-modify operation will call the separate query and modify methods and be synchronized.
  • If the query and modify operations are not synchronized, you also might restrict their visibility to package or private level.
  • That way you have a safe, synchronized operation decomposed into two easier-to-understand methods.
  • These lower-level methods then are available for other uses.

Replace Parameter with Explicit Methods

If the caller wants to decide what it wants to do by setting the parameter, it might as well call different methods instead of passing an instructive parameter to one compound method and let the big method run conditional operations.

Preserve Whole Object

Preserve Whole Object means when a method needs value(s) from an object, instead of passing the value(s), pass the whole object.

Key consideration: Dependency Structure

Passing in the required object causes a dependency between the required object and the called object.
If you don’t want to introduce this dependency, don’t pass the whole object.

Is it worth it when only one value is needed?

Martin’s opinion is yes.

  • One value and one object amount to the same thing when you pass them in, at least for clarity’s sake
  • There may even be a performance cost with pass by value parameters.

    Is your method in the right place?

    That a called method uses lots of values from another object is a signal that the called method should really be defined on the object from which the values come. When you are considering Preserve Whole Object, consider Move Method as an alternative.

Introduce Parameter Object

When you see a group of values that tend to be passed together, consider grouping them together by creating a new class, then pass its instances as parameter instead,

  • This will shorten the parameter list and improve readability.
  • Once you have the new class, you might find that the new class is a better home for the method. Moving the method to the class holding data it is more interested in will largely reduce duplicated code.

Replace Constructor with Factory Method

In Java/C++, you have to know the exact class of the object you want to create, when using constructors. To go around this, you could use Factory Methods.

Direct motivation: Subclassing instead of Type Code

Constructors can only return an instance of the object that is asked for. So you need to replace the constructor with a factory method.

Encapsulate Downcast

When you’ll need to downcast

In Java, he lack of templates means that you have to downcast whenever you take an object out of a collection.
In other strongly typed languages, you’ll also need to do downcast when the compiler could not figure out the specific type of the object.

Do not leave the evil job of downcast to clients

If you return a value from a method, and you know the type of what is returned is more specialized than what the method signature says, you are putting unnecessary work on your clients.
Rather than forcing them to do the downcasting, you should always provide them with the most specific type you can.

Replace Error Code with Exception

Exceptions are better than returning error codes because they clearly separate normal processing from error processing. This makes programs easier to understand.

Deciding if the exception is checked or unchecked

  • If the caller is responsible for testing the condition before calling, make the exception unchecked.
    • If it is the caller’s responsibility to make a check before calling, it will be a programming error if the requirement is not met. Therefore it is a bug*m and an *unchecked exception should be thrown.
  • If the exception is checked, either create a new exception or use an existing one.
    • If the routine inside the method body will check for certain requirements, I’ll need to declare the exception in the interface. This way I signal the caller to expect this type of exception, and take appropriate measures.

============= 17/8/2020 =============

Dealing with Generalization

Duplication gets me into trouble.

Pull Up Field

Visibility change: If the fields used to be private, you’ll need to make it protected in the updated superclass so that

Pull Up Method

Duplicate behaviour is the a breeding ground for future bugs: Hard to make safe changes.

Challenge with Pulling up Method: reference to subclass features

The most awkward element of Pull Up Method is that the body of the methods may refer to features that are on the subclass but not on the superclass.

  • If the feature is a method, you can:
    • generalize the other method
    • create an abstract method in the superclass
      • If the feature is a field, you can:
    • pull up the field
    • see if Template Method will move down the part of the method that refer to the subclass field
      • If you have two methods that are similar but not the same, you may be able to use Form Template Method.

Pull Up Constructor Body

  • You can access the superclass’s constructor via super() in subclass constructors.
  • If refactoring becomes complex, you might want to consider Replace Constructor with Factory Method instead.

Extract Subclass/Extract Superclass

Extract Subclass Triggers:

  • A class has behavior used for some instances of the class and not for others.
  • Type Code, but you don’t have a subclass yet to do Replace Type Code with Subclasses/Strategy/State

Extract Superclass Triggers:

  • Two classes doing similar things

Alternative: Extract Class

A choice between Delegation and Inheritance.

Extract Interface

Triggers

  • Use of only a particular subset of a class’s responsibilities by a group of clients.
  • A class needs to work with any class that can handle certain requests.

Collapse Hierarchy

When the superclass and subclass are doing the same thing.

Form Template Method

This was covered in CompSci718 and has been very handy.
Inheritance is a powerful tool for eliminating duplicate behavior.

Replace Inheritance with Delegation

“Has a” relationship: The Stack(delegate) has a Vector object in its field, and “speaks” on behalf of Vector.

This relates to the concepts of Proxy Pattern that I learnt from CompSci711. Here’s the link to my learning note about Proxy Pattern. They are different because:

  • There is not inheritance/implementation hierarchy in Delegation itself.
  • The Stack class cannot pretend it is a Vector.
    But they are similar because:
  • The “delegator” hides the details of the delegated class from the client.
  • Applying the delegation/proxy reduces the burden of re-implementing the same functionality of the existing delegated/subject.

Replace Delegation with Inheritance

From “has a/depend” relationship to “extends” relationship.

If you are tired of writing the delegating methods.

You should not make a subclass that does not inherit all superclass’s behaviours. Other options:

  • Extract Superclass/Interface to extract the common parts first
  • Let the client call the delegated subject directly by using Remove Middle Man.

======= 19/8/2020: Big Refactorings =======

Tease Apart Inheritance

Create two hierarchies and use delegation to invoke one from the other.

The PizzaStore nightmare I had with CompSci718 was a good example on this. By teasing apart the region styles and the flavour, the code was more alterable whenever I need to add a new flavour/region: It is a nice matrix.

Convert Procedural Design to Objects

I had a vivid experience on this refactoring with my CompSci711 assignment (Echo - sequential).
I got more flexibility to achieve distributed echoing if I encapsulate the procedural communications into Node objects and let them maintain their own state.

“Using objects” is more than calling constructors. The typical situation is long procedural methods on a class with little data and dumb data objects with nothing more than accessors.

Scenarios when you do need classes with behaviours/procedures and little/no data: Strategy objects! These are dedicated for varying behaviours. They are usually small and are used when we have a particular need for flexibility.

Key process:

  • Take each long procedure and apply Extract Method and the related refactorings to break it down.
  • As you break down the procedures, use Move Method to move each one to the appropriate dumb data class.

Separate Domain from Presentation

Some GUI classes contain domain logic. Take the domain logic out as a separate class.

domain-from-pre.png

After the refactoring, your presentation class will know about the domain class, but the domain class does not need to know about presentation.

MVC Pattern

Key value of MVC: separation between the code for user interface (View) and the domain logic (Model). The presentation class in only responsible for user interface; The domain class only contains business logic.

Extract Hierarchy

When you have a class doing a lot of jobs, and part of the jobs are done with lots of conditional statements.

In evolutionary design(I made a separate node learning about evolutionary design here), it is common to think of a class as implementing one idea and come to realize later that it is really implementing two or three or ten.

======= 20/8/2020: Refactoring, Reuse, and Reality =======

This chapter is written by William Opdyke to discuss what refactor does in the reality of developer’s works.

The cost of refactoring?

People may argue that “I pay for new functions of the programme”.
Refactoring might seem expensive for no new features, but it proves itself by saving the efforts and time in other stages of programme development.

Refactor securely

The intuitive definition: “Secure refactoring” is refactoring that do not cause damages to the programme. The purpose of refactoring is to change the structure of the programme without changing its behaviour.

William claims this section to be the most valuable part of this chapter.

The options you have:

  • believe in your coding ability
  • believe in your compiler
  • believe in your testing suite
  • believe your code reviewers

But people make mistakes. Compilers miss semantic bugs. Tests do not cover all cases.

William’s suggestion: define and prototype a refactoring tool to check whether a refactoring can be safely applied to a program and, if it is, refactor the program.

Refactoring Tools

By the sound of what “refactoring browser” does, what we have with Jetbrain suite/Visual Studio is pretty powerful as refactoring tools nowadays.

Here’s my screenshot of what Rider has to offer:

Technical Criteria for a Refactoring Tool

Program Database

Creating a database requires using semantic analysis (parsing) to determine the “part of speech” of every token in the program.

This must done at:

  • class definition level, to determine instance variable and method definitions,
  • the method level, to determine instance variable and method references.

Parse Trees

A parse tree is a data structure that represents the internal structure of the method itself.

Here’s a parse tree example provided by Martin:

Accuracy

The refactorings implemented by a tool must reasonably preserve the behavior of programs.

Practical Criteria for a Refactoring Tool

Speed

If the analysing of the programme takes too much time upfront, developers might not find it an efficient tool.
A simple solution: Ask programme for the information the tool will need. Now programmers shoulder some responsibilities for potential errors, but it makes the tool more usable.

Undo

Yes I want this so much!

Integration with other tools

The accessibility of the refactoring tool makes a lot of differences.

🎈🎈🎈🎈🎈 Done! (20 Aug 2020) 🎈🎈🎈🎈🎈

Today I finish reading the book!

Important things I learnt:

  • Why I refactor
  • When I should refactor
  • Why I should not hurry to optimise my programme too early
  • What are the bad smells calling for refactoring
  • How design patterns are applied in refactoring
  • That I should be aware of my “two hats” - I should not suddenly become the developer and start changing the code behaviour while I’m wearing the refactoring hat.

Impressive refactoring techniques I learnt:

  • Adding delegation, and removing it when not needed.
  • Introducing Method Object
  • Extract Methods, for the purpose of filling the semantic gap between what the method does and how it does it.
  • Replace conditional with polymorphism + Replace type code with subclass

Impressive bad smells that I am now more aware of:

  • Dumb data class
  • Shotgun Surgery
  • Speculative Generality
  • Refused Bequest
  • Feature Envy

I want to work on:

  • I am having confusion on how much (if any) I should leave spaces for future changes in my programme, or should I just focus on what is currently needed.
    • I read the assignment2-implementation1 code and I see myself through it: trying so hard to give spaces for future changes that hazards are introduced for the current purposes.