SOLID 객체 지향 설계 원칙

김정훈·2024년 6월 6일

Java

목록 보기
44/48

SOLID

S) Single Responsibility Principle 단일 책임 원칙

하나의 클래스는 하나의 책임(기능, 서비스)을 가짐

O) Open Closed Principle 개방 폐쇄 원칙

확장에는 열려있고, 변화에는 닫혀 있음

  • 디자인패턴 : 전략패턴
  • 상속(extend)은 확장에 불리
  • 구성이 확장에 유리
  • 직접 구성 객체를 생성 ❌ 👉 통제 불가
  • 통제를 위해서 구성 객체의 주입 개방 👉 생성자 매개변수(외존 관계), Setter 메서드(연관관계)를 통해서

개방원칙

확장에 있어 클래스를 상속받을경우 하위클래스에서는 필요하지 않은 자원들을 상속받아야 할 때가 있다.(유연성 떨어짐)
자원들을 구상클래스들로 설계하여 다른클래스가 구상클래스들을 객체화시켜 사용할 수 있도록 하는게 유연성에 있어 유리하다.

상속

public class Computer{
	public void monitor(){}
    
    public void touchScreen(){}
    
    public void body(){}
    
    public void keyBoard(){}
    
    public void mouse() {}
}

public class Desktop extends{
	//컴퓨터의 모든 자원들을 상속받게 된다.
}

구상

public class Keyboard{
}

public class Monitor{
}

public class TouchScreen{
}

public class Body{
}

public class Mouse{
}

public class Desktop{
	private Monitor monitor = new Monitor();
    private Body body = new Body();
    private Keyboard keyboard = new Keyboard();
    private Mouse mouse = new Mouse();
    
    public void run(){
    	//monitor, body, keyboard, mouse 객체를 활용.
    }
}

public class Tablet{
	private TouchScreen touchScreen = new TouchScreen();
    private Body body = new Body();
    
    public void run(){
    	//touchScreen, body 객체를 사용..
    }

만약 데스크탑의 모니터를 변경했을 때, 다형성을 통해
모니터를 업그레이드 할 수 있다. 그러나, 백개의 데스크탑이 있다고 가정하면 일일히 모든 데스크탑의 모니터를 업그레이드 해야만한다는 번거로움이 발생.
그렇기 때문에 유연하게 통제하기 위해 객체를 직접생성하기보다는 외부에서 구조에대한 의존성을 주입받는 방식으로 한다.
의존성이란?
어떤 클래스가 인스턴스화 되기 위해 필요하로 하는 다른 클래스의 객체.

다형성을 통한 객체변경

public class Desktop{
	//private Monitor monitor = new Monitor();
    //다형성을 통한 변경.
    private Monitor monitor = new UpgradeMonitor();
    private Body body = new Body();
    private Keyboard keyboard = new Keyboard();
    private Mouse mouse = new Mouse();
    
    public void run(){
    	//monitor, body, keyboard, mouse 객체를 활용.
    }
}

외부에서 주입받는 방식
생성자의 매개변수로 구성하여 외부에서 객체를 주입받는다. 의존하는 객체들을 내부에서 생성하는 것이 아니라 외부에서 생성하여 주입한다.(개방)

public class Desktop{
	private Monitor monitor; 
    private Body body;
    private Keyboard keyboard;
    private Mouse mouse;
    
    public Desktop(Monitor monitor, Body body, Keyboard keyboard, Mouse mouse){
    this.monitor = monitor;
    this.body = body;
    this.keyboard = keyboard;
    this.mouse = mouse;
    }
    
    public void run(){
    	//monitor, body, keyboard, mouse 객체를 활용.
    }
}

public class Ex01{
	public static void main(String[] args){
    	Desktop desktop = new Desktop(new Monitor, new Body, new Keyboard, new Mouse)
        //의존하는 객체들을 내부에서 생성하는 것이 아니라 외부에서 생성하여 주입한다.(개방)

그러나, 위 코드 처럼 객체를 생성하게 되면 의존하는 객체들을 모두 생성하여 조립해야하는 번거로움이 발생하기 때문에 조립을 수행해주는 클래스를 만들어준다.

  1. 조립클래스 내부에 생성매서드를 정의한다 : 의존하고있는 객체를 필요할때마다 호출하여 객체로 생성해주는 메서드를 각각 정의한다.
  2. 조립클래스 내부에 조립메서드를 정의한다 : 조립을 필요로하는 클래스를 생성하기 위해 의존하고있는 객체들은 위에서 정의해둔 메서드를 통해 의존하고있는 객체를 생성하고 조립하여 매개변수로 넘겨준다.
public class ComputerAssembler{
	public Monitor monitor(){
    	return new Monitor();
    }
    public Body body(){
    	return new Body();
    }
    public Monitor monitor(){
    	return new Monitor();
    }
    public Keyboard keyboard(){
    	return new Keyboard();
    }
    public Mouse mouse(){
    	return nenw Mouse();
    }
    
    public Desktop desktop(){
    	return new Desktop(monitor(), body(), keyboard(), mouse());
    }
}

public class Ex02{
	public static void main(String[] args){
    	ComputerAssembler assembler = new CoumputerAssembler();
        Desktop d1 = assembler.desktop();
        Desktop d2 = assembler.desktop();
	}
}

폐쇠원칙

public class Desktop{
	private Monitor monitor; 
    private Body body;
    private Keyboard keyboard;
    private Mouse mouse;
    
    public Desktop(Monitor monitor, Body body, Keyboard keyboard, Mouse mouse){
    this.monitor = monitor;
    this.body = body;
    this.keyboard = keyboard;
    this.mouse = mouse;
    }
    
    public void run(){
    	//monitor, body, keyboard, mouse 객체를 활용.
        body.run();
        //Desktop 객체는 구성 객체(monitor, body, keyboard, mouse)의 변화에 영향을 받는 관계
        👉 통제가 필요  👉 캡슐화 : 폐쇄
    }
}

L) 리스코프 치환 원칙

서브타입은 언제나 기반 타입으로 교체할 수 있음.(다형성)
하위 클래스 자료형 -> 상위 클래스의 자료형

I) Interface Segregation Principle 인터페이스 분리 원칙

하나의 인터페이스에 설계 항목을 너무 많이 ❌, 역할별로 인터페이스를 분리

추상 클래스를 상속받을 경우 반드시 추상클래스의 메서드를 오버라이딩 해야함. 또한 상속을 하나밖에 받지 못한다.
그렇기 때문에 인터페이스를 설계하는것이 더 유리하다.

추상클래스

public abstract class Bird{
	public void fly(){
    
    }
    public void speak(){
    
    }
}

public class Eagle extends Bird{
	@Override
    public void fly(){
    	System.out.println("훨훨");
    }
    
    @Override
    public void speak(){
    	System.out.println("짹짹");
    }
}

public class Chicken extends Bird{
	@Override
    public void fly(){
    
    }
    
    @Override
    public void speak(){
    
}

인터페이스

public interface Speakable{
	void speak();
}

public class Eagle implements Flyable{
	@Override
    public void fly(){
    	System.out.println("훨훨");
    }

public class Chicken implements Speakable(){
	System.out.println("꽥꽥");
    }

D) Dependency Inversion Principle 의존 역전 원칙

특정 대상을 직접 참조하기 보다는 그 대상의 상위 요소인 추상클래스 또는 인터페이스로 참조
예) 컬렉션 프레임워크

ArrayList<String> items = new ArrayList<>();❌
List<String> items = new ArrayList<>();⭕️
profile
안녕하세요!

2개의 댓글

comment-user-thumbnail
2024년 6월 19일

자바 맛집이라고 해서 왔어염 ㅇ.<

1개의 답글