본문 바로가기
Java

객체지향 5원칙 - SOLID

by 대우니 2024. 4. 1.
728x90
반응형

서론

객체지향에는 5원칙이 있는데, Single Responsibility, Open Close, Liscov Substitution, Interface Segregation, Dependency Inversion(SOLID)이다. 코드를 통해 해당 원칙을 살펴보도록 하자.

단일 책임 원칙(Single Responsibility Principle)

클래스는 단 하나의 책임을 가져야 하며 어떤 클래스를 변경해야하는 이유는 오직 하나뿐이어야 한다.

속성, 메서드, 패키지, 모듈, 컴포넌트, 프레임워크 등에 적용

보안 클래스에서는 두 기능을 모두 가지고 있어 단일 책임 원칙을 위반하고 있다.

// 단일 책임 원칙을 지키지 않았을 때
public class Authentication {

    private String authentication;

    public void setAuthentication(String authentication) {
        this.authentication = authentication;
    }

    public void certificateForAdmin () {
    }
		public void certificateForBasic () {
		}
}

다음의 클래스에서는 하나의 클래스에 한가지 기능을 가지고 있다.

// 단일 책임 원칙을 지켰을 때
public interface Authentication {
    abstract void certificate();
}

class Admin implements Authentication {
    @Override
    void certificate() {
    }
}

class Basic implements Authentication {
    @Override
    void certificate() {
    }
}

개방 폐쇄 원칙 (Open Close Principle)

객체 확장은 개방적, 객체 수정은 폐쇄적

기능이 변하거나 확장되는 것은 가능하나, 그 과정에서 기존의 코드가 수정되지 않아야 함.

→ 객체간의 의존성을 최소화 하여 코드 변경에 따른 영향력을 낮춤

소프트웨어 엔티티(클래스, 모듈, 함수 등)은 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다.

개방 폐쇄 원칙 위배 코드

public class NOT_OCP고기 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		삼겹살 고기 = new 삼겹살();
		  고기.roast();
		
		꽃등심 고기2 = new 꽃등심();
		  고기2.roast();
		
		안심 고기3 = new 안심();
		  고기3.roast();
	}

}

class 삼겹살{	
	void roast() {
		System.out.println("삼겹살을 굽는다.");
	}
}

class 꽃등심{	
	void roast() {
		System.out.println("꽃등심을 굽는다.");
	}
}

class 안심{	
	void roast() {
		System.out.println("안심을 굽는다.");
	}
}

개방 폐쇄 원칙 적용 코드

public class OCP고기 {
	public static void main(String[] args) {
		
		고기[]고기집 = new 고기[3];
		
		고기집[0] = new 삼겹살();
		고기집[1] = new 꽃등심();
		고기집[2] = new 안심();
		
		for(int i=0; i<고기집.length; i++) {
			고기집[i].roast();
		}

	}
}
class 고기{
	String meat="고기";
	void roast() {
		System.out.printf("나는 %s 를 굽는다. \\n",  meat);
	}
}

class 삼겹살 extends 고기{
	public 삼겹살() {
		meat = "삼겹살";
	}	
}

class 꽃등심 extends 고기{	
	public 꽃등심() {
		meat = "꽃등심";
	}
}

class 안심 extends 고기{	
	public 안심() {
		meat = "안심";
	}
}

여기에 고기를 하나 더 추가한다고 하면,

class 갈비살 extends 고기{
	public 갈비살() {
		meat = "갈비살";
	}
}

새로운 고기를 추가했음에도 기존 메서드나 코드가 변경되지 않았다.

또한 JDBC를 사용하는 클라이언트는 데이터베이스가 오라클에서 MySQL로 바뀌더라도 Connection을 설정하는 부분 외에는 따로 수정할 필요가 없다.

리스코프 치환 원칙 (Liscov Substitution Principle)

서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다.
  • 하위 클래스 is a kind of 상위 클래스 → 카테고리 구별을 위한 관계

동물 뽀로로 = new 펭귄()

  • 구현 클래스 is able to 인터페이스 → 기능을 포함시키기 위한 관계

Admin admin = new Authentication()

상속이 조직도나 계층도 형태로 구축되는 경우는 잘 구현되지 않은 경우이다.

아버지 춘향이 = new 딸()

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

인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리해야 한다.

  • 일반적인 한 개의 인터페이스보다 구체적인 여러가지의 인터페이스를 구현하는 원칙

ISP 위반 인터페이스

class S3 implements ISmartPhone {
    public void call(String number) {
    }

    public void message(String number, String text) {
    }

    public void wirelessCharge() {
        System.out.println("지원 하지 않는 기능 입니다.");
    }

    public void AR() {
        System.out.println("지원 하지 않는 기능 입니다.");
    }

    public void biometrics() {
        System.out.println("지원 하지 않는 기능 입니다.");
    }
}

ISP를 지킨 인터페이스

interface IPhone {
    void call(String number); // 통화 기능
    void message(String number, String text); // 문제 메세지 전송 기능
}

interface WirelessChargable {
    void wirelessCharge(); // 무선 충전 기능
}

interface ARable {
    void AR(); // 증강 현실(AR) 기능
}

interface Biometricsable {
    void biometrics(); // 생체 인식 기능
}
javaclass S21 implements IPhone, WirelessChargable, ARable, Biometricsable {
    public void call(String number) {
    }

    public void message(String number, String text) {
    }

    public void wirelessCharge() {
    }

    public void AR() {
    }

    public void biometrics() {
    }
}

class S3 implements IPhone {
    public void call(String number) {
    }

    public void message(String number, String text) {
    }
}

의존 역전 원칙(Dependency Inversion Principle)

고차원 모듈은 저차원 모듈의 구현에 의존하면 안된다.

• 변하지 않는 객체에 의존한다.(상위 클래스, 인터페이스, 추상 클래스일 수록 변하지 않을 가능성이 높음.)

자동차가 구체적인 타이어들이 아닌 추상화된 타이어 인터페이스에만 의존하게 함으로써 다른 구체적인 타이어로 변경해도 자동차는 영향을 받지 않는다.

자신보다 변하기 쉬운 것에 의존하지 마라.

반응형

'Java' 카테고리의 다른 글

[Java] JVM 구조  (0) 2022.05.02
[Java] JDK & JRE & JVM  (0) 2021.05.25