Skip to content

Instantly share code, notes, and snippets.

@deman777
Forked from sizovs/EffectiveJavaExercises.java
Last active January 7, 2021 19:19
Show Gist options
  • Save deman777/19273f89c66cd822cb295dfdcf6290cf to your computer and use it in GitHub Desktop.
Save deman777/19273f89c66cd822cb295dfdcf6290cf 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 {
boolean isFree() {
try {
throw new IOException();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
// 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() {
validate(form);
var username = new Username(form.username());
validate(username);
var password = new Password(form.password());
validate(password);
createUser(username, password);
}
private void createUser(Username username, Password password) {
var user = new User(username, password);
user.save(repo);
DomainEvents.publish(new RegistrationCompleted(user));
}
private void validate(Password password) {
var isWeakPassword = password.satisfies(new IsWeak(repo));
if (isWeakPassword) {
throw new RegistrationException(WEAK_PASSWORD);
}
}
private void validate(Username username) {
if (username.satisfies(new IsTaken(repo))) {
throw new RegistrationException(USERNAME_TAKEN);
}
}
private void validate(RegistrationForm form) {
if (isEmpty(form.username()) || isEmpty(form.password()){
throw new RegistrationException(MISSING_CREDENTIALS);
}
}
}
// 3. eliminate getters and setters and turn Member into an object
member.add(offer);
// 4. Find missing domain objects and reify (thingify) them.
class LoanApplication {
public final BigDecimal amount;
public final int term;
}
class Borrower() {
private final Map<LocalDateTime, Id> loans;
public apply(LoanApplication application) {
...
}
}
// 5. Find a missing domain objects and reify (thingify) it.
class MortageApplication {
}
class Risk {
public Risk(MortageApplication application) {
...
}
boolean isTolerable() {
...
}
boolean isEquivalent(Risk otherRisk) {
...
}
}
// 6. Find a missing domain objects and reify (thingify) it.
class BankruptcyProbability {
private final decimal value;
BankruptcyProbability(Business business) {
this.value = ...
}
boolean isHigh()
}
// 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 Csv<T extends Line> {
Csv(File location) {
...
}
public Collection<T> get();
}
// 8. Replace a procedural design (and an agent noun Pinger) with an object
interface Ping {
void send();
}
// 9. Replace a procedural design (and an agent noun MoneyFormatter) with an object
interface Money {
String format();
}
class Xml<T> {
private final T value;
private byte[] bytes;
private String string;
public Xml(T value) {
this.value = value;
}
public byte[] bytes() {
if (bytes == null) {
try(var outStream = new ByteArrayOutputStream()) {
new JaxbMarshaller(value.getClass()).marshallObject(value, outStream);
bytes = outStream.toByteArray();
}
}
return bytes;
}
public String string() {
if (string == null) {
...
}
return string;
}
}
// 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 with(Permissions permissions) {
this.permissions = Optional.of(permissions);
}
}
// 13. Because of CQS, a naming conflict might arise. Fix it! (no getters & setters allowed).
user.banState() // 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 Blacklist {
boolean add(BlacklistRequest request);
}
interface BlacklistRequest {
String email();
String ipAddress();
}
// 15. Turn this procedure into an object "AuthenticationToken"
interface AuthenticationToken {
String AuthenticationToken(String username, String password) throws UserDoesNotExistException; // returns authentication token
}
// 16. fix naming issues
interface Suite {
interface Test {
void print();
boolean successful()
}
void runAndWait();
Collection<Test> tests();
}
suite.runAndWait()
for (Test test : suite.tests()) {
if (!test.successful() ) {
// pretty printing
test.print();
}
}
// 17. Can you SLAP (Single Level of Abstraction Principle) it?
boolean destroyButtonAvailable =
widgets
.stream()
.filter(Widget::isButton)
.filter(byLabel("Destroy The World"))
.findAny()
.isPresent();
Predicate<Widget> byLabel(String label) {
button -> button.label().equals("Destroy The World")
}
// 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() {
Stream.of(
Stream.of(firstName),
nickName.stream(),
Stream.of(lastName)
)
.flatMap(identity())
.collect(joining(" "));
}
}
// 19.
// from variable names, omit words that are deductable from the context
void openNewBankAccount() {
var limits = WithdrawalLimits.defaults(env);
var holder = new AccountHolder(...);
var account = new BankAccount(holder, limits);
account.open();
account.deposit(openingBonus());
repository.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 raw;
private SecurePassword(String raw) {
this.raw = raw;
}
public String raw() {
return raw;
}
public static SecurePassword from(Vault vault) {
return new SecurePassword(vault.verySecurePassword());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment