Clean Code , Not really

Almas Abdrazak
4 min readJan 7, 2019

--

Hello . In this weak I started work on one open source project , in simple words it’s non blockchain based cryptocurrency .So I want to provide some design decisions which I made during the work to make the code more elegant and understandable .

Validation

All projects need to validate the data , whether it html form or database table. How people usually do it ? For example you have a class which represent your table in database (for example User) . If you Java programmer I’m 100% sure you will use javax.validation API.

import javax.validation.constraints.*;

public class User {

@NotBlank(message = "Name cannot be null")
private String name;

@AssertTrue
private boolean hasWork;

@Size(min = 10, max = 500, message
= "Description should be in range of 10 and 500 symbols")
private String description;

@Min(value = 18, message = "Age should be greater than 18")
@Max(value = 100, message = "Age should be less than 100")
private int age;

@NotNull
@Email(message = "Email must be valid")
private String email;

// setters and getters
}

It’s all right if you not familiar with this specification , just by looking to this code you intuitively guess what it does . In Python you can use Cerberus library to do the same things.

>>> v.schema = {'amount': {'type': 'integer', 'coerce': int}}
>>> v.validate({'amount': '1'})
True
>>> v.document
{'amount': 1}

But User is not a class instead of this it’s just a data structure(See my article about structure vs class)

Let’s look at example from my open source project . I have an interface named transaction

Transaction

And one implementation

public final class TriadaTxn implements Transaction {

private static final Pattern PREFIX_P = Pattern.compile("^[a-zA-Z0-9]+$");

private static final Pattern DETAILS_P = Pattern.compile("^[a-zA-Z0-9 @!?*_\\-.:,'/]+$");

@Delegate
private final Transaction origin;

public TriadaTxn(final int id,
final Date date,
final TxnAmount amount,
final String prefix,
final WalletId bnf,
final String details) {
TriadaTxn.vaildate(id, date, amount, bnf, details, prefix);
...
);
}

private static void vaildate(final int id,
final Date date,
final TxnAmount amount,
final WalletId bnf,
final String details,
final String prefix) {
TriadaTxn.validateId(id);
TriadaTxn.validateDate(date);
TriadaTxn.validateAmount(amount);
TriadaTxn.validatePrefix(prefix);
TriadaTxn.validatePrefix(prefix);
TriadaTxn.validateBnf(bnf);
TriadaTxn.validateDetails(details);

}
}

As you can see body and signature of Transaction were created using a lot of arguments such as id,date,amount and so on.This class keeps all these data and validate them in Constructor(Ctor). But I don’t like it because around 50 lines of code don’t do anything except for validating and it became harder to see main logic among huge validation logic . How did I solve it ? As Transaction is just an interface you can create as many realization as you want / I have used design pattern named DECORATOR to decorate TriadaTxn with validation logic.

ValidatedTxn
Main transaction class

What was changed?

  1. make TriadaTxn as package friendly so nobody outside of package can’t create an instance of this class
  2. create class with name ValidatedTxn which is also implements Transaction and validate all data in Ctor
  3. If data is valid , ValidatedTxn delegate methods of Transaction using TriadaTxn. The main idea is — you can’t create instance of TriadaTxn but instead , you use ValidatedTxn which give you TriadaTxn’s instance if your data is correct.

DTO

Let’s return to TriadaTxn class , in some places I need to know amount of money in transaction or Date when transaction was created . We pass these params to Ctor but don’t have access after object creation. How to solve it ? One of the Simplest solution — just create getters.So our class will looks like this

Triada with getters

Two limitations

  1. You can’t create instance of TriadaTxn directly ,dut to it you need to add these methods to ValidatedTxn or better to transaction interface but doing this you tethering interface and realization
  2. Transaction became a kind of structure , no encapsulation

My solution was pretty simple . create another class which can get this information from TriadaTxn.

  1. We need to create an interface to represent TriadaTxn data
Data

2. Create a realization in the same package to have access to this interface

public final class ParsedTxnData implements TriadaTxn.Data {
private final int id;
private final Date date;
private final TxnAmount txnAmount;
private final String prefix;
private final String details;

public ParsedTxnData(final Transaction txn) {
final JsonObject jsonObject = txn.asJson();
this.id = jsonObject.get("id").getAsInt();
this.date = new Date(jsonObject.get("date").getAsLong());
this.txnAmount = new TxnAmount(jsonObject.get("amount").getAsLong());
this.prefix = jsonObject.get("prefix").getAsString();
this.details = jsonObject.get("details").getAsString();
}

@Override
public int id() {
return this.id;
}

@Override
public Date date() {
return this.date;
}

@Override
public TxnAmount amount() {
return this.txnAmount;
}
}...

This class build Transaction data from json representation.How is it different from DTO? ParsedTxnData encapsulate parsing logic , it encapsulate state instead of data.

With these series I want to share how I solved problems in elegant ways.

This is the source code — https://github.com/strogiyotec/triada

Thank you for attention!!!

--

--