Dealing with Generalization — Tasks¶
12 hands-on exercises.
Task 1 ⭐ — Pull Up Method (Java)¶
abstract class Employee {
protected String name;
}
class Engineer extends Employee {
public String greet() { return "Hi, I'm " + name; }
}
class Manager extends Employee {
public String greet() { return "Hi, I'm " + name; }
}
Solution
Task 2 ⭐ — Pull Up Field (Java)¶
abstract class Employee {}
class Engineer extends Employee { protected String id; }
class Manager extends Employee { protected String id; }
Solution
Task 3 ⭐ — Push Down Method (Java)¶
abstract class Employee {
public double quota() { return 0; } // only Salesman uses
}
class Salesman extends Employee {
public double quota() { return 50000; }
}
class Engineer extends Employee {}
Solution
Task 4 ⭐⭐ — Extract Superclass (Java)¶
class Department {
private String name;
private List<Person> staff;
public double totalCost() { ... }
public String name() { return name; }
}
class Employee {
private String name;
private double salary;
public double annualCost() { return salary * 12; }
public String name() { return name; }
}
Solution
abstract class Party {
protected String name;
public String name() { return name; }
public abstract double annualCost();
}
class Department extends Party {
private List<Person> staff;
public double annualCost() { return /* totalCost() logic */; }
}
class Employee extends Party {
private double salary;
public double annualCost() { return salary * 12; }
}
Task 5 ⭐⭐ — Extract Interface (Java)¶
class Employee {
public double rate() { return 100; }
public int days() { return 5; }
}
class Contract {
public double dailyRate() { return 200; }
public int contractDays() { return 30; }
}
class Billing {
public double charge(Employee e) { return e.rate() * e.days(); }
public double charge(Contract c) { return c.dailyRate() * c.contractDays(); }
}
Solution
interface Billable {
double rate();
int days();
}
class Employee implements Billable {
public double rate() { return 100; }
public int days() { return 5; }
}
class Contract implements Billable {
public double rate() { return 200; }
public int days() { return 30; }
}
class Billing {
public double charge(Billable b) { return b.rate() * b.days(); }
}
Task 6 ⭐⭐ — Form Template Method (Java)¶
class TextStatement {
public String emit(Customer c) {
StringBuilder b = new StringBuilder();
b.append("Customer: ").append(c.name()).append("\n");
for (Order o : c.orders()) b.append(" - ").append(o.summary()).append("\n");
b.append("Total: ").append(c.total());
return b.toString();
}
}
class HtmlStatement {
public String emit(Customer c) {
StringBuilder b = new StringBuilder();
b.append("<h1>").append(c.name()).append("</h1>");
for (Order o : c.orders()) b.append("<p>").append(o.summary()).append("</p>");
b.append("<p>").append(c.total()).append("</p>");
return b.toString();
}
}
Solution
abstract class Statement {
public final String emit(Customer c) {
StringBuilder b = new StringBuilder();
b.append(header(c.name()));
for (Order o : c.orders()) b.append(line(o.summary()));
b.append(footer(c.total()));
return b.toString();
}
protected abstract String header(String name);
protected abstract String line(String summary);
protected abstract String footer(Money total);
}
class TextStatement extends Statement {
protected String header(String n) { return "Customer: " + n + "\n"; }
protected String line(String s) { return " - " + s + "\n"; }
protected String footer(Money t) { return "Total: " + t; }
}
class HtmlStatement extends Statement {
protected String header(String n) { return "<h1>" + n + "</h1>"; }
protected String line(String s) { return "<p>" + s + "</p>"; }
protected String footer(Money t) { return "<p>" + t + "</p>"; }
}
Task 7 ⭐⭐⭐ — Replace Inheritance with Delegation (Java)¶
class Stack<E> extends Vector<E> {
public void push(E e) { add(e); }
public E pop() { return remove(size() - 1); }
}
Solution
Task 8 ⭐⭐ — Collapse Hierarchy (Java)¶
class Vehicle {
protected String name;
public String name() { return name; }
}
class Car extends Vehicle {
// adds nothing
}
Solution
(Eliminate the redundant Car.)Task 9 ⭐⭐⭐ — Extract Subclass (Java)¶
class Job {
private String name;
private double unitPrice;
private int employeeId;
private boolean isInternal;
public double cost() {
return isInternal ? employeeRate(employeeId) : unitPrice;
}
public String summary() {
return name + ": $" + cost() + (isInternal ? " (internal)" : "");
}
}
Solution
abstract class Job {
protected String name;
public abstract double cost();
public String summary() { return name + ": $" + cost(); }
}
class ExternalJob extends Job {
private double unitPrice;
public double cost() { return unitPrice; }
}
class InternalJob extends Job {
private int employeeId;
public double cost() { return employeeRate(employeeId); }
public String summary() { return super.summary() + " (internal)"; }
}
Task 10 ⭐⭐⭐ — Pull Up Constructor Body (Java)¶
class Manager extends Employee {
private int grade;
public Manager(String name, String id, int grade) {
this.name = name;
this.id = id;
this.grade = grade;
}
}
class Engineer extends Employee {
private int level;
public Engineer(String name, String id, int level) {
this.name = name;
this.id = id;
this.level = level;
}
}
Solution
abstract class Employee {
protected final String name;
protected final String id;
public Employee(String name, String id) {
this.name = name;
this.id = id;
}
}
class Manager extends Employee {
private final int grade;
public Manager(String name, String id, int grade) {
super(name, id);
this.grade = grade;
}
}
class Engineer extends Employee {
private final int level;
public Engineer(String name, String id, int level) {
super(name, id);
this.level = level;
}
}
Task 11 ⭐⭐ — Sealed types + Pattern matching (Java 21+)¶
abstract class Shape {
public abstract double area();
}
class Circle extends Shape {
private double r;
public double area() { return Math.PI * r * r; }
}
class Square extends Shape {
private double side;
public double area() { return side * side; }
}
Refactor to sealed records.
Solution
Task 12 ⭐⭐⭐ — Combined refactoring (Go)¶
In Go (no inheritance):
type Engineer struct {
Name string
Salary float64
}
func (e Engineer) Greet() string { return "Hi, I'm " + e.Name }
func (e Engineer) Salary() float64 { return e.Salary }
type Manager struct {
Name string
Salary float64
Grade int
}
func (m Manager) Greet() string { return "Hi, I'm " + m.Name }
func (m Manager) Salary() float64 { return m.Salary }
Solution
type Employee struct {
Name string
Salary float64
}
func (e Employee) Greet() string { return "Hi, I'm " + e.Name }
type Engineer struct {
Employee // embed
}
type Manager struct {
Employee // embed
Grade int
}
// Now both have Greet (promoted) and access to Name, Salary.
// Engineer can override or just inherit.
Self-check¶
- ☑ I can pull up duplicated members and push down specific ones.
- ☑ I can choose between Extract Superclass and Extract Interface.
- ☑ I can apply Form Template Method.
- ☑ I can convert wrong inheritance to delegation.
- ☑ I can model in Go (or other languages without inheritance).