Skip to content

Instantly share code, notes, and snippets.

@sizovs
Last active January 8, 2021 09:29
Show Gist options
  • Save sizovs/18dccb51ba675e7cc61d2717246399c1 to your computer and use it in GitHub Desktop.
Save sizovs/18dccb51ba675e7cc61d2717246399c1 to your computer and use it in GitHub Desktop.
// 1. refactor the code so it's at the same level of abstraction (SLAP).
0.
Range range = new Range(8000, 8005);
var port = Port.freeWithin(range);
1.
int from = 8000;
int to = 9000;
IntStream
.rangeClosed(from, to)
.mapToObj(Port::new)
.filter(wrap().predicate(Port::isFree))
.findFirst();
class Port {
// throws IOException on a network connection failure.
boolean isFree() throws IOException {
...
}
}
2.
int from = 8000;
int to = 9000;
IntStream
.rangeClosed(from, to)
.mapToObj(Port::new)
.filter(Port::isFree)
.findFirst();
class Port {
boolean isFree() {
...
}
}
// 2 Refactor code to a higher (and single) level of abstraction
class Registration {
private final RegistrationForm form;
private final Repository repo;
Registration(RegistrationForm form, Repository repo) {
this.form = form;
this.repo = repo;
}
void complete() {
throwIfMissingCredentials(form);
var username = new Username(form.username());
throwIfTaken(username);
var password = new Password(form.password());
throwIfWeak(password);
var user = saveUser(username, password);
publishRegistrationCompletedEvent(user);
}
}
// 3. eliminate getters and setters and turn Member into an object
member.make(offer);
member.numberOfActiveOffers();
// member.setNumberOfActiveOffers(member.getNumberOfActiveOffers() + 1);
// 4. Find missing domain objects and reify (thingify) them.
class Borrower
Loans existingLoansByDate;
Loan applyForALoan(LoanApplication application) {
}
// 5. Find a missing domain objects and reify (thingify) it.
class Risk {
private final BigDecimal risk;
Risk(MortageApplication mortgageApplication) {
this.risk = calculate(...);
}
boolean isTolerable() {
...
}
boolean isEquivalentTo(Risk another) {
...
}
}
// 6. Find a missing domain objects and reify (thingify) it.
class BankruptcyProbability {
BankruptcyProbability(Business business) { ... }
boolean isHigh(RiskPolicy policy) { ... }
}
// 7. Replace a procedural design (and an agent noun CsvParser) with an object. Also, make sure the new design doesn't violate CQS (queries = nouns, commands = verbs).
class CsvFile<T extends Line> {
CsvFile(Path path)
Stream<T> lines() {
}
Streamable<T> lines() { // Protonpack
}
}
new CsvFile(path).lines().filter(...).forEach(...);
// 8. Replace a procedural design (and an agent noun Pinger) with an object
interface Ping {
void send();
}
interface Host {
void ping();
}
// 9. Replace a procedural design (and an agent noun MoneyFormatter) with an object
interface MoneyFormatter {
String format(Money money);
}
class IsoFormattedMoney {
IsoFormattedMoney(Money money) {
this.money = money;
}
@Override
String toString() {
....
}
}
money.isoFormatted(); -> IsoFormattedMoney()
money.xxxFormatted(); -> XxxFormattedMoney()
System.out.println("You have: " + new IsoFormattedMoney(money));
// 10. Turn XmlMarshaller into a class Xml
// + Make the class generic (decouple it from Invoice)
// + Use type inference
// + Use try-with-resources block
class Xml {
Xml(Object entity) { ... }
byte[] bytes() {
try (var stream = new ByteArrayOutputStream()) {
var jaxb = new JaxbMarshaller(entity.getClass());
jaxb.marshallObject(entity, stream);
return outStream.toByteArray();
}
}
}
xml.asBytes();
xml.bytes();
// 11. /validate/ method is a query (returns the result), but it sounds like an action. Fix it!
interface Input {
boolean isValid();
}
// 12. make Permissions "optional", ditch a setter and provide a domain-specific command.
class User {
private Optional<Permissions> permissions = Optional.empty();
void grant(Permissions permissions) {
this.permissions = Optional.of(permissions);
}
void revokePermissions() {
this.permissions = Optional.empty();
}
}
// 13. Because of CQS, a naming conflict might arise. Fix it! (no getters & setters allowed).
user.currentBan() // returns user's ban and the corresponding information (if any)
user.putBan() // bans a user
// 14. Can you spot an object that pretends as a service? Fix it!
interface Blacklist {
boolean contains(WebsiteVisitor visitor);
}
interface WebsiteVisitor {
String email();
String ipAddress();
}
// 15. Turn this procedure into an object "AuthenticationToken"
class AuthenticationToken {
AuthenticationToken(String username, String password) throws UserDoesNotExistException
asString()
}
// 16. fix naming issues
interface Suite {
interface Test {
void prettyPrint();
boolean hasFailed()
}
void runAndWait();
Stream<Test> tests();
}
suite.runAndWait()
suite.tests().filter(Test::hasFailed).forEach(Test::prettyPrint)
// 17. Can you SLAP (Single Level of Abstraction Principle) it?
boolean destroyButtonAvailable =
widgets
.stream()
.filter(Widget::isButton)
.map(Button::label)
.anyMatch("Destroy The World"::equals);
// 18.
// implement the /fullName/ method so that it:
// 1. returned "firstName lastName" if nickname is missing
// 2. returned "firstName <nickname> lastName" if nickname is present.
// For example: "Robert Martin" or "Robert <Uncle Bob> Martin"
class User {
private final Optional<String> nickName;
private final String firstName;
private final String lastName;
String fullName() {
var nick = nickName
.map(nick -> " <" + nick + "> ")
.orElse(" ");
return firstName + nick + lastName;
return nickName
.map(nick -> firstName + " <" + nick + "> " + lastName)
.orElseGet(() -> firstName + " " + lastName);
}
}
// 19.
// from variable names, omit words that are deductable from the context
void openNewBankAccount() {
var limits = WithdrawalLimits.defaults(env);
var accountHolder = new AccountHolder(...);
var account = new BankAccount(accountHolder, limits);
account.open();
account.deposit(openingBonus());
account.save(repository);
}
var password = new SecurePassword(vault);
throw RuntimeException();
password.toString(); // 123
password.toString(); // 123
password.toString(); // 123
password.toString(); // 123
// 20.
// calling a logic (such as remote system call) in a constructor not always a good idea. Do you know how to fix that?
class SecurePassword {
private final Supplier<String> rawPassword;
private SecurePassword(Vault vault) {
this.rawPassword = memoized(vault::verySecurePassword);
}
@Override
public String toString() {
return rawPassword.get();
}
}
class SecurePassword {
private final ConcurrentHashMap<Vault, String> cache = new ConcurrentHashMap<>(1, 1.0f);
private SecurePassword(Vault vault) {
this.vault = vault;
}
@Override
public String toString() {
return cache.computeIfAbsent(vault, vault::verySecurePassword);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment