JavaRush /Java Blog /Random-KO /Java의 디자인 패턴
Viacheslav
레벨 3

Java의 디자인 패턴

Random-KO 그룹에 게시되었습니다
패턴이나 디자인 패턴은 개발자 작업에서 종종 간과되는 부분으로, 코드를 유지 관리하고 새로운 요구 사항에 적응하기 어렵게 만듭니다. 이것이 무엇인지, JDK에서 어떻게 사용되는지 살펴보시기 바랍니다. 당연히 어떤 형태로든 모든 기본 패턴은 오랫동안 우리 주변에 있었습니다. 이번 리뷰에서 살펴보겠습니다.
Java의 디자인 패턴 - 1
콘텐츠:

템플릿

공석에 대한 가장 일반적인 요구 사항 중 하나는 "패턴에 대한 지식"입니다. 우선, "디자인 패턴이란 무엇입니까?"라는 간단한 질문에 답할 가치가 있습니다. 패턴은 영어에서 "템플릿"으로 번역됩니다. 즉, 이것은 우리가 어떤 일을 하는 특정 패턴입니다. 프로그래밍에서도 마찬가지이다. 일반적인 문제를 해결하기 위한 몇 가지 확립된 모범 사례와 접근 방식이 있습니다. 모든 프로그래머는 건축가입니다. 클래스를 몇 개 또는 하나만 생성하더라도 변화하는 요구 사항에 따라 코드가 얼마나 오래 지속될 수 있는지, 다른 사람이 사용하는 것이 얼마나 편리한지에 따라 달라집니다. 그리고 이것이 템플릿에 대한 지식이 도움이 되는 부분입니다. 왜냐하면... 이를 통해 코드를 다시 작성하지 않고도 코드를 작성하는 최선의 방법을 빠르게 이해할 수 있습니다. 아시다시피 프로그래머는 게으른 사람이고 여러 번 다시 실행하는 것보다 즉시 잘 작성하는 것이 더 쉽습니다.) 패턴도 알고리즘과 비슷하게 보일 수 있습니다. 그러나 차이점이 있습니다. 알고리즘은 필요한 조치를 설명하는 특정 단계로 구성됩니다. 패턴은 접근 방식만 설명할 뿐 구현 단계는 설명하지 않습니다. 패턴이 다르기 때문에.. 다양한 문제를 해결하세요. 일반적으로 다음 범주가 구별됩니다.
  • 생성

    이러한 패턴은 객체 생성을 유연하게 만드는 문제를 해결합니다.

  • 구조적

    이러한 패턴은 객체 간의 연결을 효과적으로 구축하는 문제를 해결합니다.

  • 행동

    이러한 패턴은 객체 간의 효과적인 상호 작용 문제를 해결합니다.

예제를 고려하려면 온라인 코드 컴파일러 repl.it을 사용하는 것이 좋습니다.
Java의 디자인 패턴 - 2

창조 패턴

객체 생성부터 객체 수명주기의 시작부터 시작해 보겠습니다. 생성 템플릿은 객체를 보다 편리하게 생성하고 이 프로세스에 유연성을 제공하는 데 도움이 됩니다. 가장 유명한 것 중 하나는 " 빌더 "입니다. 이 패턴을 사용하면 복잡한 개체를 단계별로 만들 수 있습니다. Java에서 가장 유명한 예는 다음과 같습니다 StringBuilder.
class Main {
  public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(',');
    builder.append("World!");
    System.out.println(builder.toString());
  }
}
객체를 생성하는 또 다른 잘 알려진 접근 방식은 생성을 별도의 메서드로 이동하는 것입니다. 이 메서드는 말하자면 개체 팩토리가 됩니다. 그래서 이 패턴을 " 팩토리 메소드(Factory Method)"라고 부릅니다. 예를 들어 Java에서는 클래스에서 그 효과를 볼 수 있습니다 java.util.Calendar. 클래스 자체 Calendar는 추상적이며 이를 생성하려면 다음 메소드가 사용됩니다 getInstance.
import java.util.*;
class Main {
  public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());
    System.out.println(calendar.getClass().getCanonicalName());
  }
}
이는 객체 생성 이면의 논리가 복잡할 수 있기 때문인 경우가 많습니다. 예를 들어 위의 경우 기본 클래스에 액세스하면 Calendar클래스가 생성됩니다 GregorianCalendar. 생성자를 보면 조건에 따라 서로 다른 구현이 생성되는 것을 볼 수 있습니다 Calendar. 그러나 때로는 하나의 팩토리 메소드만으로는 충분하지 않습니다. 때로는 서로 맞도록 서로 다른 개체를 만들어야 하는 경우도 있습니다. 또 다른 템플릿인 " 추상 팩토리 "가 도움이 될 것입니다. 그리고 한 곳에 다양한 공장을 만들어야 합니다. 동시에 구현 세부 사항이 우리에게 중요하지 않다는 장점이 있습니다. 우리가 어떤 특정 공장을 얻는지는 중요하지 않습니다. 가장 중요한 것은 올바른 구현을 생성한다는 것입니다. 슈퍼 예:
Java의 디자인 패턴 - 3
즉, 환경(운영 체제)에 따라 호환 가능한 요소를 생성하는 특정 공장을 갖게 됩니다. 다른 사람을 통해 만드는 접근 방식의 대안으로 " Prototype " 패턴을 사용할 수 있습니다. 그 본질은 간단합니다. 이미 존재하는 객체의 이미지와 유사성으로 새로운 객체가 생성됩니다. 그들의 프로토타입에 따르면. Java에서는 모든 사람이 이 패턴을 접하게 됩니다. 이는 인터페이스를 사용하는 것입니다 java.lang.Cloneable.
class Main {
  public static void main(String[] args) {
    class CloneObject implements Cloneable {
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return new CloneObject();
      }
    }
    CloneObject obj = new CloneObject();
    try {
      CloneObject pattern = (CloneObject) obj.clone();
    } catch (CloneNotSupportedException e) {
      //Do something
    }
  }
}
보시다시피 호출자는 clone. 즉, 프로토타입을 기반으로 객체를 생성하는 것은 객체 자체의 책임입니다. 이는 사용자를 템플릿 객체 구현에 묶지 않기 때문에 유용합니다. 음, 이 목록의 마지막 패턴은 "싱글턴" 패턴입니다. 그 목적은 간단합니다. 전체 애플리케이션에 대해 개체의 단일 인스턴스를 제공하는 것입니다. 이 패턴은 멀티스레딩 문제를 자주 보여주기 때문에 흥미롭습니다. 더 자세히 알아보려면 다음 기사를 확인하세요.
Java의 디자인 패턴 - 4

구조적 패턴

객체를 생성하면서 더 명확해졌습니다. 이제는 구조적 패턴을 살펴볼 때입니다. 그들의 목표는 지원하기 쉬운 클래스 계층 구조와 관계를 구축하는 것입니다. 첫 번째이자 잘 알려진 패턴 중 하나는 " Deputy "(Proxy)입니다. 프록시는 실제 개체와 동일한 인터페이스를 가지므로 클라이언트가 프록시를 통해 작업하든 직접 작업하든 차이가 없습니다. 가장 간단한 예는 java.lang.reflect.Proxy 입니다 .
import java.util.*;
import java.lang.reflect.*;
class Main {
  public static void main(String[] arguments) {
    final Map<String, String> original = new HashMap<>();
    InvocationHandler proxy = (obj, method, args) -> {
      System.out.println("Invoked: " + method.getName());
      return method.invoke(original, args);
    };
    Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
        original.getClass().getClassLoader(),
        original.getClass().getInterfaces(),
        proxy);
    proxyInstance.put("key", "value");
    System.out.println(proxyInstance.get("key"));
  }
}
보시다시피, 예제에는 원본이 있습니다. 이것은 HashMap인터페이스를 구현하는 것 입니다 Map. 다음으로 및 메소드 HashMap를 호출하는 클라이언트 부분의 원래 프록시를 대체하는 프록시를 생성하고 호출 중에 자체 논리를 추가합니다. 보시다시피 패턴의 상호 작용은 인터페이스를 통해 발생합니다. 그러나 때로는 대체품만으로는 충분하지 않습니다. 그런 다음 " Decorator " 패턴을 사용할 수 있습니다. 데코레이터는 래퍼 또는 래퍼라고도 합니다. 프록시와 데코레이터는 매우 유사하지만 예제를 보면 차이점을 알 수 있습니다. putget
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    List<String> list = new ArrayList<>();
    List<String> decorated = Collections.checkedList(list, String.class);
    decorated.add("2");
    list.add("3");
    System.out.println(decorated);
  }
}
프록시와 달리 데코레이터는 입력으로 전달되는 항목을 자체적으로 감쌉니다. 프록시는 프록시되어야 하는 항목을 수락하고 프록시된 개체의 수명을 관리할 수도 있습니다(예: 프록시된 개체 만들기). 또 다른 흥미로운 패턴이 있습니다 - " 어댑터 ". 데코레이터와 유사합니다. 데코레이터는 하나의 개체를 입력으로 사용하고 이 개체에 대한 래퍼를 반환합니다. 차이점은 기능을 변경하는 것이 아니라 하나의 인터페이스를 다른 인터페이스에 적용하는 것이 목표라는 것입니다. Java에는 이에 대한 매우 명확한 예가 있습니다.
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    String[] array = {"One", "Two", "Three"};
    List<String> strings = Arrays.asList(array);
    strings.set(0, "1");
    System.out.println(Arrays.toString(array));
  }
}
입력에는 배열이 있습니다. 다음으로 배열을 인터페이스로 가져오는 어댑터를 만듭니다 List. 작업할 때 실제로는 배열을 사용하여 작업하는 것입니다. 따라서 요소를 추가하면 작동하지 않습니다. 왜냐하면... 원래 배열은 변경할 수 없습니다. 그리고 이 경우에 우리는 를 얻게 될 것입니다 UnsupportedOperationException. 클래스 구조 개발에 대한 다음 흥미로운 접근 방식은 복합 패턴입니다 . 하나의 인터페이스를 사용하는 특정 요소 집합이 특정 트리형 계층 구조로 배열된다는 점이 흥미롭습니다. 상위 요소에서 메서드를 호출하면 필요한 모든 하위 요소에서 이 메서드가 호출됩니다. 이 패턴의 대표적인 예는 UI(java.awt 또는 JSF)입니다.
import java.awt.*;
class Main {
  public static void main(String[] arguments) {
    Container container = new Container();
    Component component = new java.awt.Component(){};
    System.out.println(component.getComponentOrientation().isLeftToRight());
    container.add(component);
    container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    System.out.println(component.getComponentOrientation().isLeftToRight());
  }
}
보시다시피 컨테이너에 구성 요소를 추가했습니다. 그런 다음 컨테이너에 구성 요소의 새로운 방향을 적용하도록 요청했습니다. 그리고 컨테이너는 자신이 어떤 구성 요소로 구성되어 있는지 알고 이 명령의 실행을 모든 하위 구성 요소에 위임했습니다. 또 다른 흥미로운 패턴은 " 브리지(Bridge )" 패턴입니다. 이는 서로 다른 두 클래스 계층 간의 연결 또는 브리지를 설명하기 때문에 이렇게 불립니다. 이러한 계층 중 하나는 추상화로 간주되고 다른 하나는 구현으로 간주됩니다. 이는 추상화 자체가 작업을 수행하지 않고 이 실행을 구현에 위임하기 때문에 강조됩니다. 이 패턴은 "컨트롤" 클래스와 여러 유형의 "플랫폼" 클래스(예: Windows, Linux 등)가 있을 때 자주 사용됩니다. 이 접근 방식을 사용하면 이러한 계층 구조(추상화) 중 하나가 다른 계층 구조(구현)의 개체에 대한 참조를 수신하고 해당 계층에 주요 작업을 위임합니다. 모든 구현은 공통 인터페이스를 따르기 때문에 추상화 내에서 상호 교환될 수 있습니다. Java에서 이에 대한 명확한 예는 다음과 같습니다 java.awt.
Java의 디자인 패턴 - 5
자세한 내용은 " Java AWT의 패턴 " 문서를 참조하세요 . 구조적인 패턴 중 ' 파사드(Facade )' 패턴 도 주목하고 싶습니다 . 그 본질은 편리하고 간결한 인터페이스 뒤에 이 API 뒤에 있는 라이브러리/프레임워크 사용의 복잡성을 숨기는 것입니다. 예를 들어, JPA의 JSF 또는 EntityManager를 예로 사용할 수 있습니다. " 플라이웨이트 " 라는 또 다른 패턴도 있습니다 . 그 본질은 서로 다른 객체가 동일한 상태를 갖는 경우 일반화하여 각 객체가 아닌 한 곳에 저장할 수 있다는 것입니다. 그러면 각 객체는 공통 부분을 참조할 수 있게 되어 저장을 위한 메모리 비용이 절감됩니다. 이 패턴에는 사전 캐싱 또는 개체 풀 유지 관리가 포함되는 경우가 많습니다. 흥미롭게도 우리는 처음부터 이 패턴을 알고 있습니다.
Java의 디자인 패턴 - 6
같은 비유로 문자열 풀이 여기에 포함될 수 있습니다. 이 주제에 대한 기사인 " 플라이웨이트 디자인 패턴 " 을 읽을 수 있습니다 .
Java의 디자인 패턴 - 7

행동 패턴

그래서 우리는 객체가 어떻게 생성되는지, 클래스 간의 연결이 어떻게 구성되는지 알아냈습니다. 남은 가장 흥미로운 점은 객체의 동작을 변경할 때 유연성을 제공하는 것입니다. 그리고 행동 패턴이 이에 도움이 될 것입니다. 가장 자주 언급되는 패턴 중 하나는 " 전략 " 패턴입니다. 『 Head First. Design Patterns 』 책의 패턴 연구가 시작되는 곳이다. "전략" 패턴을 사용하면 작업을 수행하는 방법을 객체 내부에 저장할 수 있습니다. 내부 객체는 코드 실행 중에 변경될 수 있는 전략을 저장합니다. 이것은 비교기를 사용할 때 자주 사용하는 패턴입니다.
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Comparator<String> comparator = Comparator.comparingInt(String::length);
    Set dataSet = new TreeSet(comparator);
    dataSet.addAll(data);
    System.out.println("Dataset : " + dataSet);
  }
}
우리 앞에 - TreeSet. TreeSet요소의 순서를 유지하는 동작이 있습니다 . (SortedSet이기 때문에) 정렬합니다. 이 동작에는 JavaDoc에서 볼 수 있는 기본 전략인 "자연 순서" 정렬(문자열의 경우 사전순)이 있습니다. 매개변수가 없는 생성자를 사용하는 경우 이런 일이 발생합니다. 하지만 전략을 바꾸고 싶다면 패스할 수 있습니다 Comparator. 이 예에서는 세트를 로 생성할 수 있으며 new TreeSet(comparator), 그러면 요소 저장 순서(저장 전략)가 비교기에 지정된 순서로 변경됩니다. 흥미롭게도 " State " 라는 패턴이 거의 동일합니다 . "상태" 패턴은 주 개체에 이 개체의 상태에 따라 달라지는 일부 동작이 있는 경우 상태 자체를 개체로 설명하고 상태 개체를 변경할 수 있음을 나타냅니다. 그리고 주 개체의 호출을 상태에 위임합니다. Java 언어의 기본을 연구하면서 우리에게 알려진 또 다른 패턴은 " 명령 " 패턴입니다. 이 디자인 패턴은 서로 다른 명령이 서로 다른 클래스로 표시될 수 있음을 시사합니다. 이 패턴은 Strategy 패턴과 매우 유사합니다. 그러나 전략 패턴에서는 특정 작업이 수행되는 방식(예: 에서 정렬)을 재정의했습니다 TreeSet. "명령" 패턴에서는 어떤 작업이 수행될지 재정의합니다. 패턴 명령은 스레드를 사용할 때 매일 사용됩니다.
import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}
보시다시피 command는 새 스레드에서 실행될 작업이나 명령을 정의합니다. " 책임 사슬" 패턴을 고려해 보는 것도 가치가 있습니다 . 이 패턴도 매우 간단합니다. 이 패턴은 무언가를 처리해야 하는 경우 핸들러를 체인으로 수집할 수 있음을 나타냅니다. 예를 들어 이 패턴은 웹 서버에서 자주 사용됩니다. 입력 시 서버는 사용자로부터 몇 가지 요청을 받습니다. 그런 다음 이 요청은 처리 체인을 통과합니다. 이 핸들러 체인에는 필터(예: IP 주소 블랙리스트의 요청을 수락하지 않음), 인증 핸들러(인증된 사용자만 허용), 요청 헤더 핸들러, 캐싱 핸들러 등이 포함됩니다. 하지만 Java에는 더 간단하고 이해하기 쉬운 예가 있습니다 java.util.logging.
import java.util.logging.*;
class Main {
  public static void main(String[] args) {
    Logger logger = Logger.getLogger(Main.class.getName());
    ConsoleHandler consoleHandler = new ConsoleHandler(){
		@Override
            public void publish(LogRecord record) {
                System.out.println("LogRecord обработан");
            }
        };
    logger.addHandler(consoleHandler);
    logger.info("test");
  }
}
보시다시피 핸들러는 로거 핸들러 목록에 추가됩니다. logger.getHandlers로거가 처리할 메시지를 수신하면 각 메시지는 해당 로거에 대한 처리기 체인( 에서)을 통해 전달됩니다 . 우리가 매일 보는 또 다른 패턴은 “ Iterator ”입니다. 그 본질은 객체 컬렉션(즉, 데이터 구조를 나타내는 클래스, 예를 들어 List)을 분리하고 이 컬렉션을 탐색하는 것입니다.
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
보시다시피 반복자는 컬렉션의 일부가 아니지만 컬렉션을 순회하는 별도의 클래스로 표시됩니다. 반복자의 사용자는 그것이 어떤 컬렉션을 반복하고 있는지조차 알지 못할 수도 있습니다. 그는 어떤 컬렉션을 방문하고 있나요? " 방문자 " 패턴을 고려해 볼 가치가 있습니다 . 방문자 패턴은 반복자 패턴과 매우 유사합니다. 이 패턴은 개체의 구조를 우회하고 이러한 개체에 대한 작업을 수행하는 데 도움이 됩니다. 개념이 다소 다릅니다. 반복자는 컬렉션을 순회하므로 반복자를 사용하는 클라이언트는 컬렉션 내부에 무엇이 있는지 신경 쓰지 않고 시퀀스의 요소만 중요합니다. 방문자는 우리가 방문하는 개체의 특정 계층이나 구조가 있음을 의미합니다. 예를 들어 별도의 디렉터리 처리와 별도의 파일 처리를 사용할 수 있습니다. Java에는 다음과 같은 형식으로 이 패턴이 즉시 구현되어 있습니다 java.nio.file.FileVisitor.
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
  public static void main(String[] args) {
    SimpleFileVisitor visitor = new SimpleFileVisitor() {
      @Override
      public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
        System.out.println("File:" + file.toString());
        return FileVisitResult.CONTINUE;
      }
    };
    Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
    try {
      Files.walkFileTree(pathSource, visitor);
    } catch (AccessDeniedException e) {
      // skip
    } catch (IOException e) {
      // Do something
    }
  }
}
때로는 일부 개체가 다른 개체의 변경 사항에 반응해야 할 경우 "관찰자" 패턴이 도움이 될 것 입니다 . 가장 편리한 방법은 일부 개체가 다른 개체에서 발생하는 이벤트를 모니터링하고 응답할 수 있도록 하는 구독 메커니즘을 제공하는 것입니다. 이 패턴은 다양한 이벤트에 반응하는 다양한 리스너 및 관찰자에서 자주 사용됩니다. 간단한 예로, JDK의 첫 번째 버전에서 이 패턴의 구현을 떠올릴 수 있습니다.
import java.util.*;
class Main {
  public static void main(String[] args) {
    Observer observer = (obj, arg) -> {
      System.out.println("Arg: " + arg);
    };
    Observable target = new Observable(){
      @Override
      public void notifyObservers(Object arg) {
        setChanged();
        super.notifyObservers(arg);
      }
    };
    target.addObserver(observer);
    target.notifyObservers("Hello, World!");
  }
}
또 다른 유용한 행동 패턴인 " 중재자 " 가 있습니다 . 이는 복잡한 시스템에서 서로 다른 개체 간의 연결을 제거하고 개체 간의 모든 상호 작용을 중개자인 일부 개체에 위임하는 데 도움이 되기 때문에 유용합니다. 이 패턴의 가장 눈에 띄는 애플리케이션 중 하나는 이 패턴을 사용하는 Spring MVC입니다. 이에 대한 자세한 내용은 " Spring: Mediator Pattern " 에서 읽을 수 있습니다 . 예제에서도 같은 내용을 자주 볼 수 있습니다 java.util.Timer.
import java.util.*;
class Main {
  public static void main(String[] args) {
    Timer mediator = new Timer("Mediator");
    TimerTask command = new TimerTask() {
      @Override
      public void run() {
        System.out.println("Command pattern");
        mediator.cancel();
      }
    };
    mediator.schedule(command, 1000);
  }
}
이 예는 명령 패턴과 더 비슷해 보입니다. 그리고 "Mediator" 패턴의 본질은 Timer'a. 타이머 내부에는 작업 대기열 TaskQueue과 스레드가 있습니다 TimerThread. 이 클래스의 클라이언트로서 우리는 그들과 상호 작용하지 않지만 Timer객체와 상호 작용합니다. 객체는 메소드 호출에 응답하여 중개자인 다른 객체의 메소드에 액세스합니다. 외부적으로는 "Facade"와 매우 유사해 보일 수 있습니다. 하지만 차이점은 Facade를 사용하면 컴포넌트들은 Facade가 존재하는지 모르고 서로 대화를 한다는 점이다. 그리고 "Mediator"를 사용하는 경우 구성 요소는 중개자를 알고 사용하지만 서로 직접 접촉하지는 않습니다. " Template Method " 패턴을 고려해 볼 가치가 있습니다 . 패턴은 이름에서 분명합니다. 결론은 코드 사용자(개발자)에게 일부 알고리즘 템플릿이 제공되고 그 단계를 재정의할 수 있는 방식으로 코드가 작성된다는 것입니다. 이를 통해 코드 사용자는 전체 알고리즘을 작성하지 않고 이 알고리즘의 하나 또는 다른 단계를 올바르게 수행하는 방법에 대해서만 생각할 수 있습니다. AbstractList예를 들어 Java 에는 반복자의 동작을 정의하는 추상 클래스가 있습니다 List. 그러나 반복자 자체는 , , 와 같은 리프 메서드를 get사용 set합니다 remove. 이러한 메소드의 동작은 하위 항목의 개발자에 의해 결정됩니다 AbstractList. 따라서 -의 반복자는 AbstractList시트를 반복하는 알고리즘의 템플릿입니다. 그리고 특정 구현의 개발자는 AbstractList특정 단계의 동작을 정의하여 이 반복의 동작을 변경합니다. 우리가 분석하는 패턴 중 마지막은 " Snapshot "(Momento) 패턴입니다. 그 본질은 이 상태를 복원할 수 있는 능력으로 객체의 특정 상태를 보존하는 것입니다. JDK에서 가장 잘 알려진 예는 객체 직렬화입니다. java.io.Serializable. 예를 살펴보겠습니다:
import java.io.*;
import java.util.*;
class Main {
  public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("test");
    // Save State
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
      out.writeObject(list);
    }
    // Load state
    byte[] bytes = stream.toByteArray();
    InputStream inputStream = new ByteArrayInputStream(bytes);
    try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
      List<String> listNew = (List<String>) in.readObject();
      System.out.println(listNew.get(0));
    } catch (ClassNotFoundException e) {
      // Do something. Can't find class fpr saved state
    }
  }
}
Java의 디자인 패턴 - 8

결론

리뷰에서 보았듯이 패턴은 매우 다양합니다. 그들 각각은 자신의 문제를 해결합니다. 그리고 이러한 패턴을 알면 시스템이 유연하고 유지 관리 가능하며 변경에 저항할 수 있도록 작성하는 방법을 시간 내에 이해하는 데 도움이 될 수 있습니다. 마지막으로 더 자세한 내용을 알아볼 수 있는 몇 가지 링크가 있습니다. #비아체슬라프
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION