1) 클린코드
2) 리펙토링
1) SOLID란
2) 결합도와 응집도
//안 좋은 코드
class Player {
Sword s = new Sword();
Gun g = new Gun();
void attack() {
s.slash();
g.shoot();
}
}
: Player라는 하나의 클래스에서 무기를 생성하고 관리 -> 검과 총을 모두 들음 -> 여러 책임 혼합되어 응집도가 낮음.
또한 새로운 무기 추가와 교체가 어려움 -> 결합도 높음
//좋은 코드
interface Weapon {
void use();
}
class Sword implements Weapon {
public void use() {
System.out.println("칼로 베었다!");
}
}
class Gun implements Weapon {
public void use() {
System.out.println("총을 발사했다!");
}
}
class Player {
Weapon w;
public Player(Weapon w) {
// TODO Auto-generated constructor stub
this.w=w;
}
public void attack() {
w.use(); //총이든 검이든 상관없이 공격가능
}
}
: 무기를 직접 생성하는 대신 주입받음!
Player는 attack()이라는 책임 하나에 집중 -> 응집도 높아짐
무기 종류가 바뀌더라도 Player 코드는 영향이 없음 -> 결합도 낮아짐
총과 검을 Weapon이라는 인터페이스를 구현시켜 따로 클래스를 만듦으로써 사용자가 무기를 무얼 선택하든 바로 w.use만 쓰면 공격할 수 있게 됨.
이런 코드는 나중에 무기가 추가되었을 때도 따로 함수를 사용할 필요 없이 동일하게 Weapon을 구현시킨 클래스를 만들면 되기 때문에 w.use를 통해 간단하게 공격할 수 있음.
3) SOLID의 필요성
4) SOLID의 활용
1) S: 단일 책임의 원칙
//before
class ReportService {
public void generateReport() {//보고서작성}
public void sendEmail() {//이메일전송}
}
//after
class ReportGenerator{
public void generateREport(){//보고서작성}
}
class EmailSender {
public void sendEmail() {//이메일 작성}
}
: 하나의 클래스에 있던 함수들을 두개의 클래스로 분리
-> 유지 보수가 용이해짐
2) O: 개방-폐쇄의 원칙

3) L: 리스코프 치환의 원칙

4) I: 인터페이스 분리의 원칙

5) D: 의존성 역전의 원칙(DIP)

1) 제어의 역전(IoC)
2) 의존성 주입(DI)





: Coffee는 구체적인 기계(예: EspressoMachine)에 직접 의존하지 않고, 오직 "커피(머신)를 추출할 줄 안다 (brew())"라는 규칙을 가진 CoffeeMachine 인터페이스만 바라본다
-> 덕분에 에스프레소 머신이든 드립 머신이든, 필요할 때 쉽게 바꾸어 낄 수 있음!





: Coffee 메뉴 객체 생성
-> 각각의 메뉴(에스프레소, 아메리카노, 라떼)는 음료 스스로의 레시피만 알고 있음(prepare())
각각의 메뉴는 생성자에서 Machine에게 의존성 주입받음(DI)
-> 커피를 직접 바로 생성하지 않고 머신의 기능(brew)을 통해 머신의 기능을 이용함


public class Espresso implements Coffee{
CoffeeMachine c;
public Espresso(CoffeeMachine c) {
// TODO Auto-generated constructor stub
this.c=c;
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
EspressoMachine em = new EspressoMachine();
MilkFrother m = new MilkFrother();
Coffee coffee1 = new Espresso(em);
System.out.println(coffee1.prepare());
Coffee latte1 = new Latte(coffee1, m);
System.out.println(latte1.prepare());
CoffeeMaker maker = new CoffeeMaker(); //DI를 이용해 라떼 만들기
maker.setCoffee(latte1); //
maker.makeCoffee();
}
+ Coffeemaker 클래스를 통해 연관, 의존관계를 더 알아보장
// 연관관계일때!
public class CoffeeMaker {
private Coffee coffee; // 핵심!! 멤버 변수(필드)로 가지고 있음!
// 1. 연관 관계를 맺어주는 역할 (의존성 주입)
public void setCoffee(Coffee coffee) {
this.coffee = coffee;
}
// 2. 이미 맺어진 연관 관계를 사용하는 역할
public void makeCoffee() {
this.coffee.prepare();
}
}
// 의존관계일때!
public class CoffeeMaker {
// 필드(멤버 변수)가 없음!
// 메서드의 매개변수로 잠깐 받아서 씀 (잠깐 의존함)
public void makeCoffee(Coffee coffee) {
coffee.prepare();
}
}
처음에 코드 다이어그램 봤을 때는 Coffee..CoffeeMachine.. 다 모르겠고 그냥 다 합쳐서 if문으로 메뉴 구분하고 짜면 쉬운거 아닌가.. 싶었는데 SOLID원칙을 통해 짠다면 지금은 조금 코드 짤 때 힘들어도 나중에 메뉴가 더 추가되거나 코드를 수정해야할 때 더 편하겠구나 하는 걸 체감했다. 나중엔 더 복잡하고 구체적인 코드를 짜게 될텐데 이렇게 클래스를 많이 짜고 하다보면 다 머릿속에서 꼬일 것 같긴한데 .. 연습하다보면 익숙해지겠지..