즐겁게!! 자신있게!! 살아보세!!

재밌는 인생을 위하여! 영촤!

Language_Study/JAVA

[JAVA, App] 23.Design Pattern

Godwony 2020. 12. 28. 18:13
728x90
반응형

Design Pattern

  • 객체의 사용 용도에 따라 클래스를 디자인하는 것

    1.생성과 관련된 패턴

1) Singleton

  • 인스턴스를 1개만 만들 수 있도록 클래스를 디자인 하는 것
  • 관리자 클래스(Controller, Manager)나 애플리케이션 전체가 공유해야 하는 공유 데이터를 갖는 클래스 또는 서버에서 클라이언트의 요청을 처리하는 클래스 등은 일반적으로 인스턴스를 1개만 만들어서 사용
  • java.lang.Runtime 클래스가 이 패턴으로 디자인

2) Decorator Pattern

  • 외부에서 데이터를 주입받아 인스턴스를 생성하는 구조

  • 하는 일은 유사한데 일을 수행해야 하는 대상이 여러가지 종류일 때 사용하는 패턴

  • 작업을 수행하는 대상을 클래스 내부에서 직접 생성하지 않고 외부에서 생성한 후 주입받는 형태의 구조

  • java.io.BufferedReader 클래스가 Decorator Pattern의 대표적인 예

    • BufferedReader 는 문자 스트림에서 문자열을 읽어오는 스트림
    • new BufferedReader(new InputStreamReader(new FileInputStream(String filepath))): 파일에서 읽어오기
    • new BufferedReader(new InputStreamReader(Socket.getInputStream())): 소켓에서 읽어오기
    • new BufferedReader(new InputStreamReader(HttpURLConnection.getInputStream())): 웹에서 읽어오기
  • 매개변수가 여러 개가 될 수 있습니다.

  • C++ 이나 Java 는 생성자 오버로딩을 이용해서 구현하거나 매개변수로 대입되는 클래스의 다형성을 이용해서 구현

2.구조와 관련된 패턴

1) 템플릿 메소드 패턴

  • 메소드의 원형을 인터페이스(추상 클래스)에 선언하고 인터페이스를 구현한 클래스에 메소드를 구현하는 방식

  • c나 c++(추상 클래스)는 인터페이스 대신에 헤더파일을 이용해서 구현

    • 최근에는 이런 방식은 잘 사용되지 않음
  • 최근에 등장하는 GUI를 구현하는 언어들 중에도 파일을 2개로 나누어서 구현하는 경우가 있는데 이 경우는 디자인 파일과 구현 파일을 분리하는 형태이지 헤더파일을 이용하는 형태가 아닙니다.

  • 프로그래밍을 할 때 사용자의 요청을 처리하는 클래스는 이 패턴으로 디자인 합니다.

    • 이렇게 사용자의 요청을 처리하는 메소드를 소유한 클래스를 Service 클래스라고 합니다.
    • 데이터베이스 연동 프로그램에서 트랜잭션을 적용하는 지점은 Service 클래스의 메소드입니다.
  • 어떤 Unit 이라는 클래스를 설계하는데 사용자가 공격과 이동이라는 동작을 수행해야 합니다.

  • 인터페이스 생성

public interface Unit {
    //공격을 수행하는 메소드
    public void attack();
    //이동을 수행하는 메소드
    public void move();
}
  • 인터페이스를 구현한 클래스를 생성
public class UnitImpl implements Unit {

    @Override
    public void attack() {
        System.out.println("공격");
    }

    @Override
    public void move() {
        System.out.println("이동");

    }

}
  • main 메소드를 만들어서 사용
public class UnitMain {

    public static void main(String[] args) {
        //템플릿 메소드 패턴이 적용된 클래스의 인스턴스 만들기
        //변수는 인터페이스 이름을 사용하고 생성자는 클래스 이름을 이용하는 형태로 많이 작성합니다.
        Unit unit = new UnitImpl();
        unit.attack();
        unit.move();

        //List<String> list = new ArrayList<String>();
    }
}

2) Adapter Pattern

  • 구현된 인터페이스를 이용해서 상속받은 클래스의 메소드를 호출하는 패턴

  • 상속받은 클래스의 메소드를 직접 호출하기가 어려운 경우 이용

  • 상속을 받았기 때문에 상위 클래스의 메소드를 호출해서 사용하는 것이 가능한데 상위 클래스의 메소드 이름을 다른 용도로 사용하는 경우 - 상위 클래스의 메소드의 내용을 변경해서 오버라이딩 하는 경우

  • 원래 오버라이딩은 상위 클래스의 메소드 기능을 확장하기 위해서 사용하는데 확장이 아니라 변경한 경우

  • Base 클래스 생성

public class OldSystem {
    public void process() {
        System.out.println("예전 처리");
    }
}
  • Target 인터페이스 생성
public interface Target {
    //OldSystem 의 process 를 호출하기 위한 메소드
    public void legacyProcess();
}
  • Derived 클래스 생성
public class NewSystem extends OldSystem implements Target{
    @Override
    public void process() {
        //구현된 메소드의 오버라이딩: 상위 클래스의 메소드를 호출하고 기능을 추가
        /*
        super.process();
        System.out.println("기능 추가");
        */

        System.out.println("새로운 기능");
    }

    @Override
    public void legacyProcess() {
        super.process();

    }
}
  • main 메소드를 만들어서 실행
public class AdaterMain {

    public static void main(String[] args) {
        NewSystem newSystem = new NewSystem();
        //새로 만들어진 메소드
        newSystem.process();
        //이전에 만들어진 메소드
        newSystem.legacyProcess();

    }

}

3) Composite Pattern

  • 재귀적 구조를 쉽게 처리하기 위한 패턴

  • Recursive Call(Recursion - 함수가 내부에서 함수 자신을 다신 호출하는 경우)

  • 파일 시스템에는 파일과 디렉토리가 있는데 디렉토리를 삭제할려고 하는 경우 디렉토리 안의 내용을 확인해서 디렉토리이면 다시 그 안에 있는 내용들을 삭제해야 합니다.

  • 하나의 인터페이스를 만들고 인터페이스에 공통된 메소드 이름을 만들어주고 파일과 디렉토리처리를 위한 클래스를 별도로 만들어서 처리하는 메소드를 구현합니다.

    • 다형성을 구현하는 방식과 유사합니다.
  • 인터페이스는 Entry - add 와 remove 그리고 rename

  • 클래스는 File 과 Directory 로 생성

  • 인터페이스 생성

//파일과 디렉토리 클래스에서 공통으로 사용할 메소드를 소유한 인터페이스
public interface Entry {
    public void add(Entry entry);

    void remove();

    void rename(String name);
}
  • File 클래스 생성
public class File implements Entry {
    //파일 이름을 저장할 변수
    private String name;

    public File(String name) {
        this.name = name;
    }

    @Override
    public void add(Entry entry) {
        System.err.println("파일에는 파일이나 디렉토리를 추가할 수 없습니다.");
    }

    @Override
    public void remove() {
        System.out.println(name + "가 삭제되었습니다.");

    }

    @Override
    public void rename(String name) {
        this.name = name;
    }
}
  • Directory 클래스
public class Directory implements Entry {
    private String name;
    //File 이나 Directory를 소유할 수 있기 때문에 Entry를 여러 개 저장할 수 있는 자료구조를 소유
    List <Entry> list;

    public Directory(String name) {
        this.name = name;
        list = new ArrayList<Entry>();
    }

    @Override
    public void add(Entry entry) {
        list.add(entry);
    }

    @Override
    public void remove() {
        //Iterator를 이용해서 데이터 접근
        Iterator <Entry> iter = list.iterator();
        while(iter.hasNext()) {
            Entry entry = iter.next();
            //디렉토리인지 확인해서 작업을 수행을 하지 않을 수 도 있습니다.
            //File이 있으면 File의 remove를 호출하고 Directory이면 Directory의 remove를 호출 - 다형성(Polymorphism)
            entry.remove();
        }
        System.out.println("내부 데이터는 전부 삭제되었습니다.");
    }

    @Override
    public void rename(String name) {
        this.name = name;
    }
}
  • main 메소드를 만들어서 테스트
public class CompositeMain {

    public static void main(String[] args) {
        File f1 = new File("파일1");
        File f2 = new File("파일2");
        File f3 = new File("파일3");

        Directory subDirectory = new Directory("하위 디렉토리");
        subDirectory.add(f1);
        subDirectory.add(f2);

        Directory superDirectory = new Directory("상위 디렉토리");
        superDirectory.add(subDirectory);
        superDirectory.add(f3);

        superDirectory.remove();

    }

}

3.행동에 관련된 패턴

1) Command Pattern

  • 처리 내용이 비슷한 명령을 모아서 실행하는 처리가 필요할 때 명령을 인스턴스로 취급해서 처리하는 패턴

  • 데이터를 삽입하는 처리와 수정하는 처리가 필요한 경우

interface Action{
    public void execute(DTO dto);
}

class InsertAction implement Action{
    public void execute(DTO dto){
        삽입하는 코드
    }
}

class UpdateAction implement Action{
    public void execute(DTO dto){
        수정하는 코드
    }
}

//action에 대입되는 인스턴스 자체가 명령어와 유사한 역할을 수행
Action action = null;
if(command == 삽입){
    action = new InsertAction();
}else if(command == 삭제){
    action = new UpdateAction();
}
action.execute(dto);
  • 이러한 패턴은 웹 서버의 Controller 클래스를 만들어서 요청에 따라 처리를 할 때 많이 사용
    • 웹 서버 프로그래밍을 하다보면 URL에 따른 Routing 구조를 만들 때도 이 구조를 이용합니다.

2) Observer Pattern

  • 어떤 인스턴스의 내부 상태가 자주 변경되는 경우 내부 상태가 변경되는지를 감시하고 있다가 변경이 되면 알려줘서 다른 처리를 할 수 있도록 해주는 패턴
  • 알려준다고 해서 Notification 이라는 표현을 많이 사용합니다.
  • 이 패턴을 사용하는 대표적인 예가 스마트 폰의 뷰에서 키보드가 화면에서 보여지고 사라지는 것을 감시해서 뷰의 컴포넌트들을 재배치하는 형태

3) Strategy Pattern

  • 어떤 클래스가 공통된 부분이 있고 서로 다른 부분이 있는 경우 공통된 부분은 클래스 안에서 만들어 사용하고 서로 다른 부분은 외부에서 주입(Injection)받아 사용하는 패턴
public class Injection {
    private String common; //모든 인스턴스들이 "Java"라고 저장해서 사용
    private String diff1;  //인스턴스들 마다 다름
    private String diff2;  //인스턴스들 마다 다름

    public Injection(String diff1) {
        common = "Java";
        //생성자를 이용한 주입
        this.diff1 = diff1;
    }

    //diff2 에 대한 setter 메소드 - setter 나 getter를 소유한 인스턴스 변수를 property라고 합니다.
    public void setDiff2(String diff2) {
        this.diff2 = diff2;
    }

    //common 과 diff1 은 null 일 가능성이 없지만 
    //diff2는 setter를 이용해서 대입받기 때문에 null일 가능성이 존재
    //서버 프로그래밍을 할 때는 메모리 부담이 되더라도 처음부터 만들어두고 사용하는 것이 좋고
    //클라이언트 프로그래밍을 할 때는 속도가 느리더라도 필요할 때 생성하는 것이 좋습니다.
    public void disp() {
        System.out.println(common.toUpperCase());
        System.out.println(diff1.toUpperCase());
        System.out.println(diff2.toUpperCase());
    }

}

public class InjectMain {

    public static void main(String[] args) {
        Injection injection = new Injection("JavaScript");
        //다른 메소드를 호출
        injection.setDiff2("Spring");
        injection.disp();

        //setDiff2를 호출하지 않았기 때문에 diff2가 null 인 상태에서 toUpperCase를 호출해서 예외
        injection = new Injection("FrontEnd");
        injection.disp();

    }

}
  • Design Pattern은 하나의 클래스에 여러가지를 적용하기도 합니다.

  • Design Pattern의 개념은 절대적인 개념이 아니라서 개발자마다 약간씩 다르게 설명하기도 합니다.
    Singleton, Template Method, Command Pattern 은 모두 동일하게 설명합니다.

  • 이외에도 인스턴스 생성을 대신해주는 Factory Method Pattern 이나 개발자가 만든 클래스에 코드를 더해서 실제로는 개발자가 만든 클래스와 다른 형태의 인스턴스를 만들어내는 Proxy Pattern 등도 있습니다.

  • 객체지향 프로그래밍에서는 디자인 패턴이 중요합니다.

  • 객체지향 프로그래밍 언어 문법 -> 자료구조&알고리즘 -> 디자인패턴 -> SW 공학(개발 방법론 ...)

외부로부터 데이터를 받아서 사용하는 경우

  • 주입을 받는 방법은 생성자를 이용하는 방법이 있고 프로퍼티(setter)를 이용하는 방법
    • 생성자를 이용하는 방법은 처음부터 가지고 있기 때문에 NullPointerException이 발생할 가능성이 적지만 메모리에 부담이 되고 setter를 이용하는 방식은 사용하기 전에 주입받아서 사용하기 때문에 메모리를 절약할 수 있지만 NullPointerException에 대비해야 합니다.

데이터 저장 및 읽기

1.로컬이나 원격에 flat file 형태로 저장

  • text 형식: txt, csv 형식 - 용량이 작다는 장점이 있지만 전체가 아닌 일부 검색은 어려움
  • xml, json 형식: 규격화 된 포맷 형식 - 인간이 알아보기 쉬움, 인간이 직접 작성도 가능, 사이즈가 커지면 용량도 커지고 알아보기도 어려워집니다.
    • 설정 내용을 저장할 때 나 실시간으로 작은 용량의 데이터를 주고받을 때 이용

2.별도의 저장 프로그램 이용

  • Database: 앞의 방법 들보다 보안이 우수하고 검색 조건을 다양하게 설정할 수 있음, 데이터 이외의 많은 것들을 저장해야 하기 때문에 오버헤드가 큼
728x90
반응형

'Language_Study > JAVA' 카테고리의 다른 글

[JAVA, App] 24.DB연결  (0) 2020.12.28
[JAVA, App] 22.Stream API  (0) 2020.12.28
[JAVA, App] 21.Lambda  (0) 2020.12.28
[JAVA, App] 20.Parsing  (0) 2020.12.28
[JAVA, App] 19.통신  (0) 2020.12.27