자바 기본 지식 (3)

리진아·2023년 1월 16일
1
post-thumbnail

✔︎ 자바 디자인 패턴 정리

어댑터, 프록시, 데코레이터 패턴은 비슷하기에 잘 구분해야 함.

1️⃣ 어댑터 패턴

ODBC/JDBC처럼 데이터베이스 시스템을 공통의 인터페이스를 이용해 조작하여 사용하는 방식이 어댑터 패턴을 이용한 것

//어댑터를 사용하기 전

//ServiceA 클래스
pubic class ServiceA{
	void runServiceA(){
    	sout("이진아1");
    }
}



//ServiceB 클래스
public class ServiceB{
	void runServiceB(){
    	sout("이진아2");
    }
}



//ClientNoAdapter 클래스
public class ClientNoAdapter{
	public static void main(String[] args){
    	ServiceA s1 = new ServiceA();
        ServiceB s2 = new ServiceB();
        
        s1.runServiceA();
        s2.runServiceB();
    }
}

//어댑터를 사용한 후

//ServiceA를 위한 변환기 AdapterServiceA
public class AdapterServiceA{
	ServiceA s1 = new ServiceA();
    
    void runService(){
    	s1.runServiceA();
    }
}



//ServiceB를 위한 변환기 AdapterServiceB
public class AdapterServiceB{
	ServiceB s2 = new ServiceB();
    
    void runService(){
    	s2.runServiceB();
    }
}
//기존의 ServiceA와 ServiceB의 메서드를 service()라고 하는 같은 이름의 메서드로 호출하여 사용할 수 있게 하는 변환기



//어댑터 패턴을 사용하는 ClientWithAdapter
public class ClientWithAdapter{
	public static void main(String[] args){
    	AdapterServiceA As1 = new AdapterServiceA();
        AdapterServiceB As2 = new AdapterServiceB();
        
        As1.runService();
        As2.runService();
    }
}

2️⃣ 프록시 패턴

프록시를 번역하면 대리자, 대변인의 의미. 프로그램에도 똑같이 프록시에게 어떤 일을 대신 시키는 것

//프록시를 적용하지 않았을 때

//Service 클래스
public class Service{
	publice String runSomething(){
    	return "프록시";
    }
}



publice class ClientNoProxy{
	public static void main(String[] args){
    //프록시를 이용하지 않은 호출
    	Service ser = new Service();
    	sout(ser.runSomething());
    }
}

//프록시를 적용했을 때

public interface IService{
	String runSomething();
}



//IService 인터페이스를 구현한 Service
public class Service implement IService{
	public String runSomething(){
    	return "프록시";
    }
}



//IService 인터페이스를 구현한 Proxy
public class Proxy implement IService{
	IService service1;
    
    public String runSomething(){
    	sout("호출에 대한 흐름 제어가 주목적, 반환 결과를 그대로 전달");
        service1 = new Service();
        return service1.runSomething();
    }
}



//프록시를 사용한 ClientWithProxy
publice class ClientWithProxy{
	public static void main(String[] args){
    //프록시를 이용한 호출
    	IService proxy = new proxy();
    	sout(proxy.runSomething());
    }
}

✔︎ 대리자는 실제 서비스와 같은 이름의 메서드를 인터페이스를 사용하여 구현

3️⃣ 데코레이터 패턴

주어진 상황 및 용도에 따라 어떤 객체에 책임(기능)을 동적으로 추가하는 패턴

Component : 실질적인 인스턴스를 컨트롤하는 역할
ConcreteComponent : Component의 실질적인 인스턴스의 부분으로 책임의 주체의 역할
Decorator : Component와 ConcreteDecorator를 동일시 하도록 해주는 역할
ConcreteDecoreator : 실질적인 장식 인스턴스 및 정의이며 추가된 책임의 주체

//component.interface
public interface component {
    String add(); //재료 추가
}



//Basecomponent.java
public class Basecomponent implements component {

    @Override
    public String add() {
        return "원두";
    }
}
//Basecomponent에서는 component를 상속받아 커피의 기본 재료가 되는 원두를 넣는 것



//Decorator.java
abstract public class Decorator implements component {
    private component coffeecomponent;
    
    public Decorator(component coffeecomponent) {
        this.coffeecomponent = coffeecomponent;
    }
    
    public String add() {
        return coffeecomponent.add();
    }
}
//Decorator는 커피의 재료들의 근간이 되는 추상클래스로 재료들은 이 Decorator를 상속받아 재료를 추가



//Water.java
//물을 추가해주는 클래스
public class Water extends Decorator {
    public Water(component coffeecomponent) {
        super(coffeecomponent);
    }
    
    @Override
    public String add() {
        return super.add() + " + 물";
    }
}
//물을 추가하는 것으로 add메소드를 정의



//Milk.java
//우유를 추가해주는 클래스
public class Milk extends Decorator {
    public Milk(component coffeecomponent) {
        super(coffeecomponent);
    }
    
    @Override
    public String add() {
        return super.add() + " + 우유";
    }
}
//우유를 추가하는것으로 add메소드를 정의



//Main.java
public class Main {
    public static void main(String[] args) {
        component espresso = new Basecomponent();
        sout("에스프레소 : " + espresso.add());
        component americano = new Water(new Basecomponent());
        sout("아메리카노 : " + americano.add());
        component latte = new Milk(new Water(new Basecomponent()));
        sout("라떼 : " + latte.add());
    }
}

->
에스프레소 : 원두
아메리카노 : 원두 + 물
라떼 : 원두 + 물 + 우유

4️⃣ 싱글턴 패턴

객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미

public class Cat {
	private static Cat instance = new Cat ();
	private Cat () {
		pubtic static Cat getInstance() { // 외부에서 가져오기 위해서 static으로 생성
        return instance;
    }
}
//static키워드로 instance생성 후 getInstance로 객체를 가져옴



public class Test {
	public static void main(String[] args) {
		Cat cat1 = Cat. getInstance ( );
		Cat cat2 = Cat. getInstance ();
        
		sout("cat1: " + cat1) ;
		sout("cat2: " + cat2);
    }
}
//인스턴스를 가져오고 싶을 경우 호출을 통해 가져옴
//2개의 객체를 생성하고 인스턴스를 가져올 경우 출력해도 동일한 값

->
cat1: classpart.Cat@28a418fc
cat2: classpart.Cat@28a418fc

5️⃣ 템플릿 메서드 패턴

어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴

✔︎ 부모 클래스에서 알고리즘의 골격을 정의하지만, 해당 알고리즘의 구조를 변경하지 않고 자식 클래스들이 알고리즘의 특정 단계들을 오버라이드(재정의)할 수 있도록 하는 행동 디자인 패턴

//템플릿 메서드 패턴을 사용하지 않았을 때

// Ramyun1.java
public class Ramyun1 {
    public void cook() {
        sout("불을 켠다.");
        sout("냄비에 물을 받고 끓인다.");
        sout("라면1을 준비한다.");
        sout("재료를 넣고 끓인다.");
        sout("불을 끈다.");
    }
}

// Ramyun2.java
public class Ramyun2 {
    public void cook() {
        sout("가스불을 켠다.");
        sout("냄비에 물을 받고 끓인다.");
        sout("라면2를 준비한다.");
        sout("재료를 넣고 끓인다.");
        sout("불을 끈다.");
    }
}

// Ramyun3.java
public class Ramyun3 {
    public void cook() {
        sout("가스불을 켠다.");
        sout("냄비에 물을 받고 끓인다.");
        sout("라면3을 준비한다.");
        sout("재료를 넣고 끓인다.");
        sout("불을 끈다.");
    }
}

//템플릿 메서드 패턴을 사용했을 때

//일부 중복되는 동작을 Ramyun 이라는 상위 클래스에 구현
abstract class Ramyun {
    public void cook() {
        sout("가스불을 켠다.");
        sout("냄비에 물을 받고 끓인다.");

        getRamyun();

        sout("재료를 넣고 끓인다.");
        sout("불을 끈다.");
    }

    protected abstract void getRamyun();
}

// Ramyun1.java
public class Ramyun1 extends Ramyun {
    @Override
    protected void getRamyun() {
        sout("라면1을 준비한다.");
    }
}

// Ramyun2.java
public class Ramyun2 extends Ramyun {
    @Override
    protected void getRamyun() {
        sout("라면2를 준비한다.");
    }
}

// Ramyun3.java
public class Ramyun3 extends Ramyun {
    @Override
    protected void getRamyun() {
        sout("라면3을 준비한다.");
    }
}

6️⃣ 팩토리 메서드 패턴

객체를 생성 반환하는 메서드

✔︎ 무신사에서 가방과 가디건을 구매하는 과정
1. 쇼핑몰(musinsa)에 들어간다
2. 카테고리를 선택한다.
3. 물건을 검색(find), 장바구니에 담고(cart), 주문(order), 마지막으로 주문을 확인(check)

//Shopping.java
public interface Shopping {
    public void find(); //물건을 검색
    public void cart(); //장바구니에 담기
    public void order(); //결제 후 주문
    public void check(); //주문이 완료되었는지 확인
} //인터페이스로 작성



//Shopping.java
public abstract class Shopping{
    public Shopping shopping(String category) {
    	Shopping shopping = selectCategory(category);    //factory method 사용
    	shopping.find();
    	shopping.cart();
    	shopping.order();
    	shopping.check();
    	return shopping;
    }

    //factory method
    abstract Shopping selectCategory(String category);
}



//musinsa.java
public class musinsa extends Shopping{
  @Override
  Shopping selectCategory(String category) {
  System.out.println("-----------musinsa-----------");
  if (category.equals("Bag")) {
  	return new BagCategory();
  } else if (category.equals("Cardigan")) {
  	return new CardiganCategory();
  }
  	return null;
  }
}
//musinsa 클래스는 Shopping클래스를 상속을 받음.
//나중에 Main 클래스에서 선언을 해주면 Shopping클래스에 기작성 되어있던 selectCategory 메서드와 쇼핑 과정들이 동작하는 것을 볼 수 있음




//BagCategory.java
public class BagCategory implements Shopping{
  @Override
  public void find() {
  	System.out.println("가방 브랜드를 검색");
  }
  @Override
  public void cart() {
  	System.out.println("가방을 장바구니에");
  }
  @Override
  public void order() {
  	System.out.println("가방을 주문");
  }
  @Override
  public void check() {
  	System.out.println("주문이 올바르게 되었는지 확인");
  }
}



//CardiganCategory.java
public class CardiganCategory implements Shopping{
  @Override
  public void find() {
  	System.out.println("가디건 브랜드를 검색");
  }
  @Override
  public void cart() {
  	System.out.println("가디건을 장바구니에");
  }
  @Override
  public void order() {
  	System.out.println("가디건을 주문");
  }
  @Override
  public void check() {
  	System.out.println("주문이 올바르게 되었는지 확인");
  }
}



//Main.java
public class Main {
  public static void main(String[] args) {
    Shopping musinsa = new musinsa();

    Shopping bag = musinsa.shopping("bag"); 
    Shopping cardigan = musinsa.shopping("cardigan"); // 식료품 카테고리
  }
}
//musinsa 클래스는 Shopping 클래스를 상속받았으므로, 기작성 된 selectCategory(메서드)와 4개의 과정들이 동작하게 됨.

//그중 가장 먼저 동작하는 selectCategory에 알맞은 매게 변수(문자열 category)를 넣어주면, 그에 따라 적절한 카테고리 객체가 반환되는 것

->
-----------musinsa-----------
가방 브랜드를 검색
가방을 장바구니에
가방을 주문
주문이 올바르게 되었는지 확인
-----------musinsa-----------
가디건 브랜드를 검색
가디건을 장바구니에
가디건을 주문
주문이 올바르게 되었는지 확인

7️⃣ 전략 패턴

같은 기능이지만 서로 다른 전략을 가진 클래스들을 각각 캡슐화하여 상호교환 할 수 있도록 하는 패턴

예시로) 커피머신에 두 가지 전략이 있다. 하나는 아메리카노 하나는 카페라떼
그러나 "커피를 내린다" 라는 기능은 같다.

public interface coffee{
	void brew();
}



public class Americano implements coffee{
	private static final String americano = "아메리카노";
	@override
	publIc String brew() {
		//아메리카노를 내리는 기능
		return americano;
    }
}



public class Cafelatte implements coffee{
	private static final string cafelatte = "카페라떼" ;
	@override
	public String brew() {
		//카페라떼를 내리는 기능
		return cafelatte;
    }
}



//커피머신
public class CoffeeMachine{
	public String brew(coffee coffee) {
		return coffee.brew() ;
    }
}

//전략패턴을 사용하면 커피머신을 아주 간결하게 구현할 수 있음
//커피 인터페이스를 받으면 클라이언트에서 주입하는 구현체에 따라 전략이 결정되기 때문.

8️⃣ 템플릿 콜백 패턴

전략 패턴의 변형으로 DI에서 사용하는 형태의 패턴

//Strategy.java
public interface Strategy{
	public abstract void runStrategy();
}



//Student.java
public class Student{
	void runContext(Strategy strategy){
    	sout("공부 시작");
        strategy.runStrategy();
        sout("공부 끝");
    }
}



//Client.java
public class Client {
	public static void main(String[] args) (
	Student takenotes = new Student();
    
	takenotes.runContext (new Strategy() {
		@Override
		public void runStrategy() {
			sout("밑줄긋기");
        }
    });
	sout();
    
	takenotes.runContext (new Strategy () {
		@Override
		public void runStrategy() {
			sout("틀린 부분 지우기");
        }
    });
	System.out.printin();
    }
}

✔︎ 중복된 코드가 있기에 템플릿 콜백 패턴으로 수정하여도 된다.



분량 정말 너무 많다.. 교재 읽는데 눈 빠질 뻔 한 것 같다.. ㅋㅋ

profile
알맹이가 가득 찬 개발자가 되기 위해 한 걸음 더 다가가는,

0개의 댓글