Clean Code Function
Function
ํจ์๋ ํ๊ฐ์ง์ผ๋ง ํด์ผํ๋ค.
ํจ์๋ ์์ง์ด์ง, ์์ ์ ์ธ, ๊ธด ์ด๋ฆ์ ๊ฐ๋ ๋ง์/์์ ํจ์๋ค๋ก ์ ์ง ํด์ผํ๋ค.
ํฐ ํจ์๋ ํด๋์ค๋ก ์ถ์ถํด์ผํ๋ค.
ํจ์๋ ๋์ด์ ์์์ง ์ ์์ ๋งํผ ์์์ผํ๋ค.
ํจ์๋ก ์ถ์ถํ๋ ๊ณผ์ ์ค์ ์ฝ๊ฐ์ ๊ท์น์ด ์๋ค
-
ํจ์๋ ์์์ง ์ ์๋ํ ์ต๋ํ ์์์ผํจ
- ํจ์ ์ ์ธ๋ฌธ์ ๋ค์ด๊ฐ์ง ์๊ณ ๋ ์ด๋คํจ์์ธ์ง ์ ์ ์๊ฒ ์์ฑ
-
if, else, while ๋ฌธ์ฅ๋ฑ์ ๋ด๋ถ ๋ธ๋ก์ ํ ์ค
- ๊ดํธ๊ฐ ์์ด์ผ ํจ
-
๋ค์ฌ์ฐ๊ธฐ๊ฐ ์ ์ด์ผํจ
- ํจ์๋ ์ค์ฒฉ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง ๋งํผ ํฌ๋ฉด ์๋จ
-
ํจ์๋ 1๊ฐ์ง์ผ๋ง ํ๋ค๋ ๊ฒ์ ๋ ์ ์ ์ฅ์ด ์๋ caller์ ์ฅ์์ ํ๊ฐ์ง
๊ฒฐ๊ตญ ํจ์ ์ถ์ํ๊ฐ ๊ฐ๋ฐ์๊ฐ ํด์ผํ ์ผ์ด๋ค. ํ์ง๋ง ์ถ์ํ๋ผ๋ ๊ฐ๋ ์ ์ ๋ง ์ฝ์ง ์๋ค. ๋๋์ฑ ๊ฐ๋ฐ ๊ฒฝํ์ด ๋ถ์กฑํ๊ฑฐ๋, ์ํํธ์จ์ด๊ณตํ์ ๋ํ ์ ๋ฐ์ ์ธ ์ง์์ด ๋ถ์กฑํ๋ฉด ๋ง์ด๋คโฆ
์ ๋ช ํ ์ฑ ์ ์ด ์ ์๋, ๊ฐ์์๋ ์ถ์ํ๋ ๋ด๊ฐ ํฌ๊ธฐ ํ ๋๊น์ง ํด์ผํ๋ค๊ณ ๋งํ๋ค.
ํนํ if, while์ด ๋ณด์ด๋ฉด, extractํ ๋์์ด๋ผ๊ณ ์์ฌ์ ํด์ผํ๋ค.
ํฌ์ธํธ
- ํจ์์ ๊ท๋ชจ๋ ์์์ผํ๋ค.
- ์ ํจ์๋ณด๋ค ์์์ผํ๋ค.
- ์ด๋ฆ์ ์์ง์ด์ผํ๋ค.
- ํจ์๋ ํ ๊ฐ์ง ์ผ๋ง ํด์ผํ๋ค.
- ๋์ด์ ์ถ์ถ ํ ์ ์์ ๋๊น์ง(๋ฐ๊ฟ๊ฒ ์ด๋ฆ ๋ง๊ณ ์์ ๋๊น์ง) ํด์ผํ๋ค.
Function Structure
1. Arguments
-
ํจ์์ ํ๋ผ๋ฏธํฐ๋ 3๊ฐ ์ดํ๋ฅผ ์งํฅํ๋ค.(๊ทธ ์ด์๊ฐ๋ฉด ์์ฑ์ ์กฐ์ฐจ๋ ๋ญ์๋์ง ์ ์๊ฐ ์๋ค.)
๋ง์ผ ์์, ๋์ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ ์จ๋ค๋ฉด ๋ฒ์ ๊ฐ์ ์ค์ ํ๋ผ๋ฏธํฐ์ ์ซ์๋ฅผ ์ค์ผ ์๊ฐ์๋ค.
ex) amountInvoicedIn(start: Data, end: Data) => amountInvoicedIn(DataRange)
์ด๋ ๊ฒ ๋๋ฉด ๊ฐ๋ ์ฑ๋ ์ข์์ง๊ณ ํ๋ผ๋ฏธํฐ๊ฐ์ด ์ ํํ ์ด๋ค๊ฑด์ง ์๊ธฐ๋ ์ฝ๋ค.
package function_structure;
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public NutritionFacts(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public NutritionFacts(int servingSize, int servings, int calories) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
์ํ์ ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ Class๊ฐ ์๋ค.
์ฌ๊ธฐ์ ๋ง์ง๋ง ์์ฑ์๋ฅผ ํธ์ถํด์ ํ๋ผ๋ฏธํฐ์ ๊ฐ๋ค์ ๋๊ฒจ ์ค๋
package function_structure;
import org.junit.Test;
public class NutritionFactsTest {
@Test
public void canCreate() {
NutritionFacts cocacola =
new NutritionFacts(240, 8, 100, 0, 35, 27);
}
}
์ด๋ฐ์์ผ๋ก ์ซ์๋ง ๋๊ฒจ์ฃผ๊ฒ ๋๋๋ฐ ์์ฑ์ ์กฐ์ฐจ๋ ํ๋ผ๋ฏธํฐ๊ฐ ๋ง์์ง์๋ก ์ด๋ค ๊ฐ์ ๋๊ธฐ๋์ง ์ด๋ ค์ ์ง๋ค.
์ฌ๊ธฐ์ Java Bean Pattern์ ์ฌ์ฉํด์ ํด๊ฒฐํ ์ ์๋ค.
package function_structure;
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters
private final int calories = 0;
private final int fat = 0;
private final int sodium = 0;
private final int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
์ด๋ฐ์์ผ๋ก Builder class๋ฅผ ๋ง๋ค์ด์ getter๋ฅผ ๋ง๋ค์ด ์๊ธฐ ์์ ์ returnํ๋ค.
์ด๋ ๊ฒ ๋ง๋ค๋ฉด
package function_structure;
import org.junit.Test;
public class NutritionFactsTest {
@Test
public void canCreate() {
NutritionFacts cocacola =
new NutritionFacts.Builder(240, 8).
calories(100).
sodium(35).
carbohydrate(27).
build();
}
}
์ด๋ ๊ฒ ๊ฐ๋ ์ฑ์ด ์ฌ๋ผ๊ฐ๋ค.
๊ฐ ํ๋ผ๋ฏธํฐ๊ฐ์ด ์ด๋ค๊ฒ์ ์๋ฏธํ๋์ง ํ๋์ ์์๋ณด๊ธฐ ์ฝ๋ค.
- Boolean ์ธ์๋ ๊ฐ๊ธ์ ์ฌ์ฉํ์ง ๋ง์
ํจ์๋ฅผ ๋ง๋ค ๋ ํด์ผํ ๊ฒ์ ์ต๋ํ์ ์ชผ๊ฐค์ ์์ ๋๊น์ง ์ชผ๊ฐ๋ ๊ฒ์ด๋ค.
๊ฐ์ด ๋ค์ด์์ true์ผ ๊ฒฝ์ฐ์ false์ผ ๊ฒฝ์ฐ๋ผ๋ 2๊ฐ์ง ๋์์ด ํจ์์ ๋ค์ด๊ฐ ์์ผ๋ฉด extract๋ฅผ ํด์ ํ์์ ๋๋๊ณ ๋ ๊ณตํต๋ ์ ์ด ์๋ค๋ฉด ๋ค์ ์ชผ๊ฐ๋ ์ด๋ฐ์์ผ๋ก ํ๋ฉด ์ฝ๋๊ฐ ๋จ์ํด์ง๊ณ ๋ณต์ก๋๋ ์ค์ด๋ค๊ฒ ๋๋ค.
- output์ธ์๋ฅผ ์ฌ์ฉํ์ง ๋ง์
์ธ์ ๊ฐ์ ๋ฐ์์ ๊ทธ ๋ณ์๋ฅผ ์ฌ์ฌ์ฉํ๋ ๊ฐ๋ฐ์๋ ์๋ค.
์ด ์ฝ๋๋ฅผ ์์ฑํ ์์ฑ์๋ ์ดํดํ ์ง ๋ชฐ๋ผ๋ ๋ค๋ฅธ ํ์๋ค์ด๋ ์ ์ง๋ณด์๋ฅผ ํ๋ ๊ฐ๋ฐ์๋ ์ดํดํ๊ธฐ ์ด๋ ค์ด ์ฝ๋๊ฐ๋๋ค. ์ด๋ด ๊ฒฝ์ฐ๋ ๋ก์ปฌ๋ณ์๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ๋ ๊ฒ์ด ํจ์จ์ ์ด๋ค.
- null๊ฐ์ ํจ์์ return ๊ฐ์ผ๋ก ์ฌ์ฉํ์ง ๋ง์
null๊ฐ๋ ๊ฒฐ๊ตญ null์ธ๊ฐ๊ณผ ์๋ ๊ฐ์ผ๋ก ๋๋์ด์ง๋ค. ์ด๋ฌ๋ฉด ์์์ ๋งํ๋ boolean๊ฐ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก 2๊ฐ์ง์ ํ์๊ฐ ์๊ธฐ๋ ๊ฒ์ด๋ค.
- ์ฝ๋์ null์ฒดํฌ, ์๋ฌ์ฒดํฌ๋ก ๋๋ฝํ์ง ๋ง์
์ฝ๋์ null์ฒดํฌ, ์๋ฌ์ฒดํฌ๊ฐ ์๋ค๋ ๊ฒ์ ๊ฐ์ ์ ์ฌ์ง๊ฐ ์๋ค๋ ์๋ฏธ์ด๊ธฐ๋ํ๋ค.
๋ ์ ์๋ฏธ๋ ํ์๋ค์ด๋, ๋จ์ ํ ์คํธ๋ฅผ ๋ชป๋ฏฟ์ด์ ์์ฑํ ๊ฒ์ด๋ผ๋ ์๋ฏธ๋ ์๋ค.
null์ฌ๋ถ์ ๋ํด์๋ ์ง์์ ์ผ๋ก ์ฒดํฌํ์ง ๋ง๊ณ , TDD ์ฆ, ๋จ์ ํ ์คํธ๋ฅผ ํด์ ๊ฒ์ฆ์ ํด์ผํ๋ค.
2. The Stepdown Rule
๋ชจ๋ public์ ์์, ๋ชจ๋ private๋ ์๋์
์ค์ํ ๋ถ๋ถ์ ์๋ก, ์์ธํ ๋ถ๋ถ์ ๋ฐ์ผ๋ก ํด์ public part๋ง ์ฌ์ฉ์์๊ฒ ์ ๋ฌํ๋ฉด ๋๋ค.
์ก์ง๋ฅผ ์์๋ก ๋ค๋ฉด, ํค๋๋ผ์ธ์ ๋จผ์ ๋ณด์ฌ์ฃผ๋ ๊ฒ๊ณผ ๊ฐ๋ค.
์ ๋ฐฉ๋ฒ์ ๋ํ ์ด์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ํธ์ง์๋ค์ ์คํด๊ฐ ์์ด ๋ํ ์ผํ ๋ถ๋ถ์ ์ ๊ฑฐํ๊ณ ํ์ ์ ์ธ ์์๋ค๋ง ์ ๋ฌ ํ ์ ์๋ค.
- ๋ ์๋ค์ ์์์ ๋ถํฐ ์ฝ๊ธฐ ์์ํ๊ณ , ์ดํด๋ฅผ ํ ๊ฒ ๊ฐ๋ค๋ฉด ๊ทธ๋ง ์ฝ์ผ๋ฉด ๋๋ค.
public void serve(socket s) {
try {
tryProcessInsertructions(s);
} catch(Throwable e) {
slimFactory.stop();
close();
closeEnclosingServiceInseperateThread();
}
}
private void tryProcessInsertructions(socket s) throws Exception {...}
์ด๋ฐ์์ผ๋ก ํจ์๊ฐ ์๋ค๋ฉด ๋ฐ๋ก ์๋์ ์์ด์ผ ๊ฐ๋ ์ฑ์ด ์ฆ๊ฐํ๋ค.
3.Switches And Cases
Switch๋ฌธ์ ์ฌ์ฉํ๋ฉด case๋ฌธ์์ ์์กด์ฑ๋ฌธ์ ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋๋๋ก์ด๋ฉด ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข๋ค.
์์กด์ฑ์ ์ต๋ํ ์ค์ฌ์ผ ๋ ๋ฆฝ์ ์ผ๋ก ๋ถ๋ฆฌ๊ฐ ๋๋ ๊ฐ์ฒด์งํฅ์ ์ธ ์ฝ๋๊ฐ ๋๋ค.
switch๋ฌธ์ ๋ฌธ์ ์ ์ case๋ฌธ์์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ฉด switch๋ฅผ ์์ ํด์ผํ๋ค.
4.Temporal Coupling
ํจ์๋ค์ด ์์๋ฅผ ์งํค๋ฉฐ ํธ์ถ๋์ด์ผํ๋ค.
์์๋ฅผ ๋ค๋ฉด,
//file should be opend before processing
fileCommand.process(file);
//file should be closed after processing
์ด๋ฐ์์ผ๋ก ํ์ผ์ ์ฒ๋ฆฌํ๊ธฐ ์ ์ ํ์ผ์ ์ด๋ ค์์ด์ผ ํ๊ณ , ํ์ผ์ ์ฒ๋ฆฌํ ํ์ ํ์ผ์ด ๋ซํ ์์ดํํ๋ค.
๋ง์ฝ, ์ฌ์ฉ์๊ฐ ์ด๋ฐํ์์ ์งํค์ง ์๊ณ ์คํํ ๊ฒฝ์ฐ ์๋ฌ๋ ๋ ์ ๋ฐ์ ์๋ค.
fileCommandTemplate.process(myfile, new FileCommand() {
public void process(File f) {
// file processing codes here
}
});
class FileCommandTemplate{
public void process(File file, FileComand command) {
file.open();
command.process(file);
file.close();
}
}
๊ฐ๋ฐ์๋ ์ด๋ฐ์์ผ๋ก ์คํํ๋ ๋ถ๋ถ์ด ๋ณต์กํ๋ฉด ๋ฐ๋ก ๋นผ๊ฑฐ๋ ํด์ ์ฌ์ฉ์๊ฐ ์๋ฌ๋ฅผ ๋ผ์ ์๋ ์ฌ์ง๋ฅผ ์ ๊ฑฐํ๋ค.
5.CQS
Command(๋ช ๋ น)ํจ์์ Query(์ง์)ํจ์๋ฅผ ๊ฐ๊ฐ ํจ์๋ช ๋๋ก ๋์, ๊ธฐ๋ฅ ํด์ผํ๋ค. ๋ง์ผ Queryํจ์์ธ๋ฐ, ๋ด๋ถ์ ๋ฐ์ดํฐ๋ฅผ ์กฐ์ํ๊ฑฐ๋, Commandํจ์์ธ๋ฐ return๊ฐ์ด ์๊ฑฐ๋ํ๋ฉด, ๊ฐ๋ฐ์์ ๋ํ ์ ๋ขฐ๊ฐ ๋จ์ด์ ธ์ ์ฝ๋๋ฅผ ์ฝ์ ๋ ๋ด์ฉ์ ์ ๋ถ ๋ด์ผ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๋ค.
๊ฐ์ฅ ์ด์์ ์ธ Side effect(์์์ธ์ ๋ฐ์)๊ด๋ฆฌ ๋ฐฉ๋ฒ
- ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ์ผ์๋ ๊ฐ์ ๋ฐํํ๋ฉด ์๋จ
- ๊ฐ์ ๋ฐํํ๋ ํจ์๋ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ฉด ์๋จ
- Command
- ์์คํ ์ ์ํ ๋ณ๊ฒฝ ๊ฐ๋ฅ
- return๊ฐ X
- side effect๋ฅผ ๊ฐ์ง๊ณ ์์(๋ณํ)
- Query
- ๊ณ์ฐ๊ฐ์ด๋, ์ํ๋ฅผ ๋ฐํ
- side effect๊ฐ ์์
User u = authorizer.login(userName, password);
๋ก๊ทธ์ธ์ ํ ๋๋ง๋ค ๊ฐ์ ๋ฐํ ๋ฐ๋๋ค. ๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด๋ฅผ ๋ฐ๊ณ ์ถ์ง ์๋๋ฐ๋ ๋ฐ์์ผํ๋ค.
authorizer.login(userName, password);
User u = authorizer.getUser(userName);
์ด๋ฐ์์ผ๋ก ์ฌ์ฉ์๊ฐ ์์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ์ง์ผ ๋ค๋ฅธ ๋๋ฃ ๊ฐ๋ฐ์๋, ๋ ์์๊ฒ ๊ฐ๋ฐ์์ ๋ํ ์ ๋ขฐ์ฑ ์๊ธด๋ค.
6.Tell, Donโt ask
ํจ์๋ฅผ ๋ง๋ค ๋ ์ค์ํ๊ฒ ์ค ํ๋๊ฐ ๋งํ๋ ๊ฒ์ด๋ค.
๋ฌด์ธ๊ฐ์๊ฒ ๋ฌผ์ด๋ณด๊ฒ ๋๋ฉด, ํจ์๋ ์ฎ์ ๊ทธ๋ฌผ์ด๋, ์ฒด์ธ๊ฐ์ ๊ตฌ์กฐ๊ฐ ๋๋ค.
o.getX()
.getY()
.getZ()
.doSomething();
์ด๋ฌ์์ผ๋กโฆ
์ด๋ค ์ฌ๋์ด ๋ด๋ ์ด๋ฐ๊ฒ์ ์ดํดํ๊ธฐ ํ๋ค๊ณ , ๊ฒฐ๊ตญ ์์กด์ฑ๋ง ๋์์ง๊ฒ ๋๋ค.
o.doSomething();
์ด๋ ๊ฒ ๋ฐ๊ฟ์ doSomething์ด ๋ค๋ฅธ ํจ์๋ฅผ ๋ ๋ถ๋ฅด๋ ์์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํด์ผํ๋ค.
7.Law of Demeter
์์ ์ฌํ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด Tell, Donโt ask์ ๋ช๊ฐ์ง ๊ท์น์ด ์๋ค.
๊ฐ์ฒด๋ ์๋์ ๋ฉ์๋๋ง ํธ์ถํ ์ ์๋ค.
- ์ธ์๋ก ์ ๋ฌ๋ ๊ฐ์ฒด
- localy ์์ฑํ ๊ฐ์ฒด
- ํ๋๋ก ์ ์ธ๋ ๊ฐ์ฒด
- ์ ์ญ ๊ฐ์ฒด
์ ๊ท์น์ ์ ์งํค๋ฉด, ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์์ง๊ณ ๋ณต์กํ ์์กด์ฑ์ ๋์ด ์ง์ ๋ฐ์ ์๋ค.
๊ฐ์ฒด๋ ์ต์ข ๋ชฉ์ ์ง๊น์ง ์ํ์ ์์ด ์ต์ข ๋ชฉ์ ์ง๊น์ง ๊ฐ๋ ๊ณผ์ ์ค ๋ค์ ๋ชฉ์ ์ง๋ง ์๋ฉด ํ ์คํธ์ฝ๋๋ฅผ ์ง๋ ๊ฒ๋ ๊ฐ๋จํด ์ง๋ค.
8.early returns
private boolean nameIsValxxxx () {
if(name.euqls(""))
return true;
if(!wikiWordWidget.xxx)
return true;
return false;
}
early return์ด๋, guarded return์ ํ์ฉ๋๋ง ๊ฐ๊ธ์ ์ฝ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ ๋ฌด์์ด ๋ ์์๊ฒ ๋ ๋์ ์ฝ๋์ธ์ง๋ฅผ ์๊ฐํ๋ฉฐ ์ง์ผํ๋ค.
ํ์ง๋ง early return์ด ๋ฌธ์ ์ธ ๊ฒ์ loop์์ ๋ฐ์ํ๋ค.
loop์์ return์ด ๋ฐ์ํ ๊ฒฝ์ฐ ๋ ์๋ ์ฝ๊ธฐ ์ด๋ ค์์ง๊ณ ํ ์คํธ๋ ํ๊ธฐ ์ด๋ ค์ ์ง๋ค. ๋์ < ์ดํด
์ฝ์ด
- CQS : Command Query Separation
์ฐธ์กฐ
- ๋ฐฑ๋ช ์์ ํด๋ฆฐ์ฝ๋ Youtube ๊ฐ์ข