Simplifying Method Calls — Tasks¶
12 hands-on exercises.
Task 1 ⭐ — Rename Method (Java)¶
getCharge is vague.
Solution
Task 2 ⭐ — Introduce Parameter Object (Java)¶
Solution
Task 3 ⭐ — Replace Parameter with Explicit Methods (Java)¶
public Result process(Order o, boolean express) {
if (express) return expressFlow(o);
return standardFlow(o);
}
Solution
Task 4 ⭐⭐ — Separate Query from Modifier (Java)¶
String getTotalAndSetReady() {
String result = computeTotal();
readyForSummary = true;
return result;
}
Solution
Task 5 ⭐⭐ — Replace Constructor with Factory Method (Java)¶
class Employee {
public Employee(int type) { this.type = type; }
static final int ENGINEER = 0, MANAGER = 1;
}
Solution
Task 6 ⭐⭐ — Replace Error Code with Exception (Java)¶
Solution
Task 7 ⭐⭐ — Replace Exception with Test (Java)¶
double getValueForPeriod(int p) {
try { return values[p]; }
catch (ArrayIndexOutOfBoundsException e) { return 0; }
}
Solution
Task 8 ⭐⭐ — Hide Method (Java)¶
public class OrderService {
public Money internalRoundingHelper(Money m) { ... } // only used internally
}
Task 9 ⭐⭐⭐ — Builder pattern for long parameter list (Java)¶
Solution
public class HttpRequest {
private final String method;
private final String url;
private final Map<String, String> headers;
// ...
public static class Builder {
private String method = "GET";
private String url;
private Map<String, String> headers = new HashMap<>();
// ...
public Builder method(String m) { this.method = m; return this; }
public Builder url(String u) { this.url = u; return this; }
public Builder header(String k, String v) { headers.put(k, v); return this; }
public Builder timeout(Duration d) { ... }
public HttpRequest build() {
Objects.requireNonNull(url, "url required");
return new HttpRequest(this);
}
}
}
// Usage:
HttpRequest req = new HttpRequest.Builder()
.method("GET")
.url("/api/users")
.header("Authorization", "Bearer ...")
.timeout(Duration.ofSeconds(5))
.build();
Task 10 ⭐⭐ — Functional Options (Go)¶
func NewServer(addr string, port int, tls bool, timeout time.Duration) *Server {
return &Server{addr: addr, port: port, tls: tls, timeout: timeout}
}
Solution
type Option func(*Server)
func WithPort(p int) Option { return func(s *Server) { s.port = p } }
func WithTLS() Option { return func(s *Server) { s.tls = true } }
func WithTimeout(d time.Duration) Option { return func(s *Server) { s.timeout = d } }
func NewServer(addr string, opts ...Option) *Server {
s := &Server{addr: addr, port: 8080}
for _, o := range opts { o(s) }
return s
}
// Usage:
srv := NewServer("0.0.0.0", WithPort(443), WithTLS())
Task 11 ⭐⭐ — Remove Setting Method (Java)¶
class Customer {
private String id;
public Customer(String id) { this.id = id; }
public void setId(String id) { this.id = id; }
}
Solution
Task 12 ⭐⭐⭐ — Combined refactoring (Python)¶
def send(user_email: str,
subject: str,
body: str,
html: bool,
retry: bool,
track: bool):
if retry:
...
if html:
...
if track:
...
Apply 3+ refactorings.
Solution
from dataclasses import dataclass
from enum import Flag, auto
class EmailFlag(Flag):
NONE = 0
HTML = auto()
RETRY = auto()
TRACK = auto()
@dataclass(frozen=True)
class Email:
to: str
subject: str
body: str
flags: EmailFlag = EmailFlag.NONE
def send(email: Email):
if EmailFlag.RETRY in email.flags: ...
if EmailFlag.HTML in email.flags: ...
if EmailFlag.TRACK in email.flags: ...
# Usage:
send(Email(
to="user@example.com",
subject="...",
body="...",
flags=EmailFlag.HTML | EmailFlag.TRACK
))
Self-check¶
- ☑ I can rename methods safely.
- ☑ I can introduce parameter objects.
- ☑ I can split queries from modifiers.
- ☑ I can replace booleans with explicit methods.
- ☑ I can pick between exception and Test for error reporting.