Skip to content

Instantly share code, notes, and snippets.

@sprour
Forked from sizovs/Unsuck.java
Last active January 7, 2021 22:56
Show Gist options
  • Save sprour/8ec4b12f0a60b4e958d1e87f27539559 to your computer and use it in GitHub Desktop.
Save sprour/8ec4b12f0a60b4e958d1e87f27539559 to your computer and use it in GitHub Desktop.
// 1. refactor the code so it's at the same level of abstraction (SLAP).
int from = 8000;
int to = 9000;
IntStream
.rangeClosed(from, to)
.mapToObj(Port::new)
.filter(Port::isFree)
.findFirst();
class Port {
// throws IOException on a network connection failure.
boolean isFree() throws IOException {
...
}
}
// 2 Refactor code to a higher (and single) level of abstraction
// 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() {
var credentials = validCredentials()
var user = newUser(credentials);
DomainEvents.publish(new RegistrationCompleted(user));
}
private Credentials validCredentials(){
if (!form.isValid()) {
throw new RegistrationException(MISSING_CREDENTIALS);
}
var username = new Username(form.username());
var isUsernameTaken = username.satisfies(new IsTaken(repo));
if (isUsernameTaken) {
throw new RegistrationException(USERNAME_TAKEN);
}
var password = new Password(form.password());
var isWeakPassword = password.satisfies(new IsWeak(repo));
if (isWeakPassword) {
throw new RegistrationException(WEAK_PASSWORD);
}
return new Credentials(username, password);
}
}
class Credentials {
public Username username;
public Password password;
public Credentials(Username username, Password password){
this.username = username;
this.password = password;
}
}
// 3. eliminate getters and setters and turn Member into an object
member.offers().add(offer);
member.activeOffers().increase();
// 4. Find missing domain objects and reify (thingify) them.
borrower.loans().apply(new Loan(amount, term));
// 5. Find a missing domain objects and reify (thingify) it.
class MortgageRiskService {
Risk calculate(MortageApplication mortgageApplication) {
...
}
boolean isTolerable(Risk risk) {
...
}
boolean areEquivalent(Risk oneRisk, Risk otherRisk) {
...
}
}
// 6. Find a missing domain objects and reify (thingify) it.
class Bankruptcy {
Probability calculate(Business business){ ... }
boolean isHigh(Probability probability){...}
}
// 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> {
Collection<T> lines;
CsvFile(File file) { ... }
Collection<T> lines() { ... }
}
// 8. Replace a procedural design (and an agent noun Pinger) with an object
interface Pinger {
void ping();
}
// 9. Replace a procedural design (and an agent noun MoneyFormatter) with an object
interface Money {
String format();
}
// 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<T> {
byte[] marshall(T object) {
try(ByteArrayOutputStream outStream = new ByteArrayOutputStream()) {
JaxbMarshaller jaxbMarshaller = new JaxbMarshaller(object.getClass());
jaxbMarshaller.marshallObject(object, outStream);
return outStream.toByteArray();
}
}
}
// 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 Permissions permissions = Permissions.DEFAULT;
void grant(Permissions permissions) {
this.permissions = permissions
}
}
// 13. Because of CQS, a naming conflict might arise. Fix it! (no getters & setters allowed).
user.banDetails() // returns user's ban and the corresponding information (if any)
user.ban() // bans a user
// 14. Can you spot an object that pretends as a service? Fix it!
interface BlacklistRequest {
String getEmail();
String getIpAddress();
boolean isBlocked();
}
// 15. Turn this procedure into an object "AuthenticationToken"
class AuthenticationToken {
static from(String username, String password) throws UserDoesNotExistException;
}
// 16. fix naming issues
interface Suite {
interface Test {
void print();
boolean isSuccessful()
}
void run();
Collection<Suite.Test> tests();
}
suite.run()
for (Suite.Test test : suite.tests()) {
if (!test.isSuccessful() ) {
// pretty printing
test.print();
}
}
// 17. Can you SLAP (Single Level of Abstraction Principle) it?
boolean destroyButtonAvailable =
widgets
.stream()
.filter(Widget::isButton)
.filter(Button::isForArmagedon)
.findAny()
.isPresent();
// 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() {
return firstName
+ " "
+ nickName.map(nickName -> "<"+nickName+"> ").orElse("")
+ lastName;
}
}
// 19.
// from variable names, omit words that are deductable from the context
void openBankAccount() {
var withdrawalLimits = WithdrawalLimits.defaults(env);
var holder = new AccountHolder(...);
var account = new BankAccount(holder, withdrawalLimits);
account.open();
account.deposit(bonus());
accountRepository.save(account);
}
// 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 String rawPassword;
private SecurePassword(String rawPassword) {
this.rawPassword = rawPassword;
}
static from(Vault vault) {
return new SecurePassword(vault.verySecurePassword());
}
public String raw() {
return rawPassword;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment