하나의 클래스는 하나의 책임(기능, 서비스)을 가짐
확장에는 열려있고, 변화에는 닫혀 있음
확장에 있어 클래스를 상속받을경우 하위클래스에서는 필요하지 않은 자원들을 상속받아야 할 때가 있다.(유연성 떨어짐)
자원들을 구상클래스들로 설계하여 다른클래스가 구상클래스들을 객체화시켜 사용할 수 있도록 하는게 유연성에 있어 유리하다.
상속
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)
//의존하는 객체들을 내부에서 생성하는 것이 아니라 외부에서 생성하여 주입한다.(개방)
그러나, 위 코드 처럼 객체를 생성하게 되면 의존하는 객체들을 모두 생성하여 조립해야하는 번거로움이 발생하기 때문에 조립을 수행해주는 클래스를 만들어준다.
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)의 변화에 영향을 받는 관계
👉 통제가 필요 👉 캡슐화 : 폐쇄
}
}
서브타입은 언제나 기반 타입으로 교체할 수 있음.(다형성)
하위 클래스 자료형 -> 상위 클래스의 자료형
하나의 인터페이스에 설계 항목을 너무 많이 ❌, 역할별로 인터페이스를 분리
추상 클래스를 상속받을 경우 반드시 추상클래스의 메서드를 오버라이딩 해야함. 또한 상속을 하나밖에 받지 못한다.
그렇기 때문에 인터페이스를 설계하는것이 더 유리하다.
추상클래스
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("꽥꽥");
}
특정 대상을 직접 참조하기 보다는 그 대상의 상위 요소인 추상클래스 또는 인터페이스로 참조
예) 컬렉션 프레임워크
ArrayList<String> items = new ArrayList<>();❌
List<String> items = new ArrayList<>();⭕️
자바 맛집이라고 해서 왔어염 ㅇ.<