Last active
February 1, 2019 08:35
-
-
Save ttrei/9d34cc701648a76ab0a09273ceb5161b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// CHALLENGE (work in pairs): | |
// Improve code snippets listed below – | |
// | |
// 1 - implement Xml class instead of XmlMarshaller | |
class XmlMarshaller { | |
byte[] marshallToXml(Invoice invoice) { | |
ByteArrayOutputStream outStream = new ByteArrayOutputStream(); | |
JaxbMarshaller jaxbMarshaller = new JaxbMarshaller(Invoice.class); | |
jaxbMarshaller.marshallObject(invoice, outStream); | |
byte[] resultXml = outStream.toByteArray(); | |
IOUtils.closeQuietly(outStream); | |
return resultXml; | |
} | |
} | |
byte[] t = new XmlMarchaller().marshallToXml(invoce); | |
//---- | |
class Xml { | |
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |
Xml(Invoice invoice) { | |
new JaxbMarshaller(Invoice.class).marchallObject(invoice, this.os); | |
} | |
byte[] toByteArray() { | |
return this.os.toByteArray(); | |
} | |
} | |
byte[] t = new Xml(invoice).toByteArray(); | |
// 2 - refactor to eliminate all null checks and branching | |
class AmazonS3File { | |
private final String path; | |
private Optional<Metadata> metadata; | |
void attach(Metadata metadata) { | |
this.metadata = Optional.of(metadata); | |
} | |
//@Nullable | |
Optional<Metadata> getMetadata() { | |
return metadata; | |
} | |
} | |
class AmazonS3Bucket { | |
void upload(AmazonS3File file) { | |
//… upload ... | |
file | |
.flatMap(File::getMetadata) | |
.map(it -> uploadMetadata(it)); | |
//if (file.getMetadata() != null) { | |
// uploadMetadata(file.getMetadata()); | |
//} | |
} | |
private void uploadMetadata(Metadata metadata) { | |
.... | |
} | |
} | |
// 4 - fix conflicting method names. No getters & setters allowed! | |
user.ban() // returns user's ban and the corresponding information (if any) | |
user.ban() // bans a user | |
//---- | |
user.banDetails() // returns user's ban and the corresponding information (if any) | |
user.ban() // bans a user | |
// user.activeBan() // word-play | |
// user.putABan() | |
// 5 – get rid of procedural design | |
interface MoneyFormatter { | |
String format(Money money); | |
} | |
//---- | |
class Money { | |
String asString(); | |
} | |
// --- | |
class ISO111MFOrmattedMoney implements FormattedMoney { ... } | |
interface FormattedMoney { | |
FormattedMoney(Money money) { | |
} | |
string toString() { | |
} | |
} | |
// | |
class PrettyMoney { | |
PrettyMoney(Money money) { | |
} | |
string toString() { | |
} | |
} | |
// 6 - implement "fullName" method, so that it returned "firstName lastName" if nickname is missing | |
// or "firstName <nickname> lastName" if nickname is present. | |
// for example – "Robert Martin" or "Robert <Uncle Bob> Martin" | |
// don't optimize prematurely (e.g. prefer simple String concatenation over StringBuilder). | |
class User { | |
private final Optional<String> nickName; | |
private final String firstName; | |
private final String lastName; | |
String fullName() { | |
return nickname | |
.map(it -> firstName + " <" + it + "> " + lastName) | |
.orElseGet(() -> firstName + " " + lastName); | |
//.orElse(firstName + " " + lastName) - will be executed regardless | |
// 9+ jvm hotspot automatically converts string concatenation into string builder | |
} | |
} | |
// 7 | |
// no setters please | |
class User { | |
private final Permissions permissions; | |
void setPermissions(Permissions permissions) { | |
this.permissions = permissions | |
} | |
} | |
class BankAccount { | |
enum Status { | |
CLOSED, OPEN | |
} | |
private final Status status; | |
void setStatus(Status status) { | |
this.status = status; | |
} | |
} | |
// ---- | |
class User { | |
private final Permissions permissions; | |
void allowAccess() { | |
this.permissions = | |
} | |
void denyAccess() { | |
this.permissions = | |
} | |
} | |
class BankAccount { | |
enum Status { | |
CLOSED, OPEN | |
} | |
private Status status; | |
void open() {this.status = Status.OPEN} | |
void close() {this.status = Status.CLOSED} | |
} | |
// 8 | |
// calling a logic (such as remote system call) in a constructor is a bad idea. Do you know how to fix that? | |
class SecurePassword { | |
private final String rawPassword; | |
private SecurePassword(Vault vault) { | |
this.rawPassword = vault.verySecurePassword(); | |
} | |
public String raw() { | |
return rawPassword; | |
} | |
} | |
//----- | |
class SecurePassword { | |
private final Optional<String> rawPassword; | |
private final Vault vault; | |
private SecurePassword(Vault vault) { | |
this.vault = vault; | |
} | |
public String raw() { | |
if (!rawPassword.ifPresent()) { | |
this.rawPassword = vault.verySecurePassword(); | |
} | |
return rawPassword; | |
} | |
} | |
// | |
class SecurePassword { | |
/* | |
@FunctionalInterface | |
interface Supplier<T> { | |
T get() {} | |
}*/ | |
private final Supplier<String> rawPassword; | |
public SecurePassword(Vault vault) { | |
// .memoize() - requires GUAVA | |
this.rawPassword = memoize(vault::verySecurePassword); | |
} | |
public String raw(){ | |
return rawPassword.get(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment