The cost of delivering a product (*) depends on the comprehensibility of the program, which results in design.
The points are the following five.
A confusing example.kt
var a = 2000
val b = if (c < 2014) 1.05 else 1.08
a = a * b
a = if (a < 2500) a * 0.8 else a
println("total${a}It is a circle.");
Easy-to-understand example.kt
val basePrice = 2000
val taxRate = if (year < 2014) 1.05 else 1.08
val afterTax = basePrice * taxRate
val result = if (afterTax < 2500) afterTax * 0.8 else afterTax
println("total${result}It is a circle.");
point --Give a variable name so that "name represents the body" --Variables have only one role --If you repeat the assignment to the variable, the range of influence of the correction will be widened. It may cause side effects. --If you repeat the assignment to the same variable even after dividing the program for each paragraph, the degree of coupling for each paragraph becomes stronger. --Separate paragraphs for each group of meanings (do not write loosely)
Thanks to the above changes, it's easier to cut out the method. First, in the same class, it is an example of extracting the process to the method.
Easy-to-understand example (repost).kt
val basePrice = 2000
val taxRate = if (year < 2014) 1.05 else 1.08
val afterTax = basePrice * taxRate
val result = if (afterTax < 2500) afterTax * 0.8 else afterTax
println("total${result}It is a circle.");
Example of extracting a method.kt
fun main(args: Array<String>) {
val basePrice = 2000
val afterTax = getAfterTaxAmount(year, basePrice)
val result = getDiscountedPrice(afterTax)
println("total${result}It is a circle.");
}
fun getAfterTaxAmount(year: Int, basePrice: Int): Double {
return basePrice * if (year < 2014) 1.05 else 1.08
}
fun getDiscountedPrice(amount: Int): Int {
return if (amount < 2500) amount * 0.8 else amount
}
The advantages of extracting methods are as follows.
The following is an example of eliminating duplicate code in different classes.
User.kt
class User(name: String, prefecture: String) {
fun isInTokyo(): Boolean {
return prefecture == "Tokyo"
}
}
Article.kt
class User(title: String, prefecture: String) {
fun isInTokyo(): Boolean {
return prefecture == "Tokyo"
}
}
The above two objects have the same value of prefecture
, and also have a method to process based on that value.
This is a duplicate code.
In this case, the refactoring policy differs between the following two methods. i. There is no reference relationship between two objects ii. There is a reference relationship between two objects
It's a good idea to create a class called Prefecture and bring duplicate logic to it.
That way, if you add, modify, or delete decision logic based on prefecture
, only the Prefecture
class will be modified. At the same time that it is easier to fix, it is less likely that unexpected bugs will occur due to omission of correction.
Prefecture.kt
class Prefecture(name: String) {
fun isInTokyo(): Boolean {
return name == "Tokyo"
}
}
ʻUser and ʻArticle
are as follows. The ʻisInTokyo` method is realized by delegating each method.
Therefore, there is no change in the client after the modification.
User.kt
class User(name: String, prefecture: Prefecture) {
fun isInTokyo(): Boolean {
return prefecture.isInTokyo()
}
}
Article.kt
class User(title: String, prefecture: Prefecture) {
fun isInTokyo(): Boolean {
return prefecture.isInTokyo()
}
}
Assuming the User has an Article, the implementation code looks like this: Make sure to call the method of the object you own. (Delegate.) (Article implementation remains the same)
User.kt
User(name: String, prefecture: String) {
private val article: Article
fun isInTokyo(): Boolean {
return article.isInTokyo()
}
}
As an extreme example, you may have seen the following code.
Basic data type.kt
class Item {
val price: Int
val quantity: Int
}
What's wrong with this is that in the above case, we declare that the allowable values for both price and quantity are -2.1 billion to +2.1 billion. Generally speaking, prices should have a reasonable upper limit. So you should define a Money class to represent it and have it in the Item class. By doing so, you can delegate the check for abnormal values that is required when processing price to the Money class, and the outlook for the code in the Item will be clearer.
For example, suppose you have a method for finding the total purchase price. In addition, it has the following restrictions:
Basic data type.kt
class Item(price: Int, quantity: Int) {
private val price: Int
private val quantity: Int
init {
this.price = price
this.quantity = quantity
}
fun calculateTotalAmount(): Int {
if (price < 0 || price > 100000) throw IllegalArgumentException()
return price * quantity
}
}
fun main(args: Array<String>) {
val price = 10000000
val item: Item = Item(price, 1)
item.calculateTotalAmount()
}
If true, all you want to do with calculateTotalAmount ()
should be one line, but it takes three times as much.
This is because I had to check the value.
It delegates this value check to the Money class.
ValueObject.kt
class Item(price: Money, quantity: Int) {
private val price: Money
private val quantity: Int
init {
this.price = price
this.quantity = quantity
}
fun calculateTotalAmount(): Int {
return price.getPrice() * quantity
}
}
class Money(price: Int) {
private val price: Int
init {
if (price < 0 || price > 100000) throw IllegalArgumentException()
this.price = price
}
fun getPrice(): Int {
return price
}
}
Now the ʻItem # calculateTotalAmount` fits in one line and there is no extra processing, so the outlook is better. Even if you are responsible for processing the object, it is logically easier for the Money class to check the amount.
_ Write tomorrow! _
Writing in Kotlin took a lot of time. .. .. I wonder if it can't be helped at first. When it comes to new languages and new information, it can be quite burdensome, so I'll keep a proper balance.