데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      [데보션영과 함께하는 책읽기-2편] 쉽게 배워 바로 써먹는 디자인 패턴

      min_gi1123 22.12.13
      1,980 20 1

      들어가기전

      DEVOCEAN YOUNG 1기 Dev-River 권민기, 김주희, 박서진, 이지수입니다.

      저희조는 다독다독 미션으로 쉽게 배워 바로 써먹는 디자인 패턴 책을 선정하여 진행하였습니다.

      저희는 24가지의 패턴을 각자의 파트를 나누어 학습한 후, 서로 학습한 내용을 발표하고 의견을 나누며 스터디를 진행하혔습니다.

      책에 대해 짧게 기술하기보다는, 저희가 배우고 기록한 내용들을 기제하는게 좋을 것 같아 조원들이 학습한 내용들을 하나로 병합하여 올립니다.


      Intro

      ☹️ 디자인책이 아닙니다.

      디자인 패턴의 주체는 디자인이 아니라 패턴 이 주체입니다.

      어? 그렇다면 디자인패턴과 패턴 은 무엇일까요.


      ♻️ 디자인 패턴과 패턴

      디자인 패턴 이란 소프트웨어 디자인 과정에서 자주 발생하는 문제들에 대한 전형적인 해결책입니다.

      패턴은 표준화 된 라이브러리처럼 ctrl x, ctrl v 가능한게 아니에요. 패턴은 코드 조각이 아니라 특정 문제를 해결하는 개념 입니다.

      특정 문제를 해결하는 개념은 알고리즘 아닌가요?

      아니에요. 비슷해보이지만 달라요!!!!!! 간단히 말하자면 알고리즘은 절차 라면, 패턴은 설계 도면 같은거에요. 응용 가능한 도면.

      다시 정리하자면 패턴은 재사용성을 가지는 객체지향 소프트웨어의 요소입니다.


      🛠️ 패턴의 분류

      패턴은 의도와 목적에 따라 분류가 나뉩니다.

      코드의 재사용성과 유연성을 증가시키는 객체 생성 메커니즘을 공유하는 생성 패턴

      객체와 클래스를 확장시키는 방법인 구조 패턴

      객체간의 효율적인 행동 처리와 분리를 위한 행동 패턴이 있습니다.


      ⚠️ 주의

      패턴을 배웠다고 막 가져다 쓰면 안돼요

      “망치만 있으면 모든 것이 못처럼 보인다.”

      패턴에 대해 배웠다고 더 간단한 일에 굳이 패턴을 사용하여 코드를 복잡하게하는 바보같은짓 하지 말라는 거에요

      디자인 패턴과 성능은 별개에요.

      디자인 패턴은 코드의 가독성과 유지보수를 위해 객체의 메서드를 분리합니다. 분리로 인하여 호출도 자주 발생하겠죠??

      함수 호출과 객체간 호출은 적을수록 성능이 좋답니다 그러니까 너무 많이 사용하지말고 적절히 사용해야 한다는 뜻이에요.


      😎 객체 지향 프로그래밍

      객체지향 프로그램의 가장 기본적인 것은 클래스를 선언하고 객체를 생성하는 것입니다. 여기서 객체 생성을 할 때는 new라는 키워드를 통해 생성합니다.

      또한 큰 규모의 응용 프로그램을 활용할 때에는 수많은 객체가 필요하고 객체들의 관계 설정도 필요합니다.

      여기서 기억해야 할 객체 지향 프로그램의 간단 정보
      1. 클래스는 선언 후 객체를 생성해야 사용 가능하다.

      2. 객체 생성은 new 키워드를 활용한다.

      3. 선언된 클래스를 객체로 생성하는 과정을 '인스턴스화'라고 부른다.

      생성패턴

      아까 객체들의 관계를 설정해주어야 한다고 언급했었잖아요?

      생성 패턴에서는 객체 간 강력한 강력한 결합 관계를 느슨한 결합으로 변경하는 설계 기법입니다. 느슨한 결합을 통해 객체 유지보수가 훨씬 더 편리하다고 합니다.

      ​Screen Shot 2022-12-02 at 4.59.18 PM.png

      생성 패턴은 위의 그림과 같은 패턴을 가지고 있는데 여기서는 '싱글턴'과 '팩토리 메서드 패턴'에 대해 자세히 알아보겠습니다.


      ⚙️ 싱글턴 패턴

      싱글이라는 이름처럼 자원 공유를 위한 객체 생성 개수를 1개로 제한하는 패턴입니다.

      다시 말하면 선언된 클래스로 복수의 객체를 생성할 수 없게 하는 거죠! 이를 통해 공유와 충돌을 방지합니다! 클래스를 이용해 객체를 생성하는 방법은 new 키워드가 유일하고 대체 불가입니다.

      Screen Shot 2022-12-02 at 5.05.05 PM.png


      간단한 예를 활용해 살펴보겠습니다.

      class Hello
      {
      	public function greeting()
          {
          	return "안녕하세요.\n";
          }
      }
      $obj =new Hello ;

      이렇게 'Hello' 라는 클래스를 먼저 선언한 후 $obj라는 객체를 new 키워드를 활용해 생성하는 것입니다.

      이를 통해 new 키워드를 통해 객체를 반복 생성할 수 있다는 것을 알게 되었습니다.

      그렇다면, 그 말은 즉슨 객체 이름만 다를 뿐 같은 클래스에 대해서 동일한 객체를 무수히 만들 수 있다는 뜻이겠죠?

      $obj1 = new Hello;

      $obj2 = new Hello;

      이런 식으로 말이죠! 그러나 이는 객체지향에서의 양날의 검이 됩니다.

      만들어진 객체는 각자의 메모리를 할당받는데 동일한 객체를 여러 메모리에 할당되어 버리면 정말 .. 별로 의미가 없지 않을까요?

      여기서 $obj1과 $obj2는 생성을 위한 클래스 선언만 같을 뿐 다른 메모리에 생성된 다른 객체라 공유도 되지 않습니다.

      그리고 다들 코딩을 해보셨다면 전역 변수와 로컬 변수에 대해 들어보셨을 겁니다.

      간단히 다시 설명하면 변수가 접근할 수 있는 영역을 구분한 것이죠. 전역 변수를 활용해 함수 간의 데이터 공유 처리를 가능하게 합니다.

      예를 들어 php에서는 전연 변수 접근을 위해 global 키워드를 제공합니다.

      함수 안에 있는 변수명 앞에 global을 붙이면 함수 밖에서도 사용가능한 전역 변수에 접근하는 것입니다.

      여기서 주의할 점은 전역 변수로 외부의 값을 공유할 때에는 변수 범위를 면밀히 관리해주어야 해 디버깅에 많은 어려움이 있다고 합니다.

      전역 변수처럼 생성한 객체를 공유하려면 하나의 객체만 존재해야 오류가 생기지 않게 됩니다.

      그래서 '싱글턴' 패턴은 1개의 객체만 생성을 제한하게 되는 거죠.

      그런데 잠깐!

      아까 new 키워드는 객체 생성을 여러 번 할 수 있다고 했는데 어떻게 1개의 객체만 생성 제한하게 할 수 있을까요?

      여기서 제한이란 new 키워드를 사용할 때 서로 다른 A,B가 생성되는 것이 아닌 동일한 객체 1개만 유지한다는 것입니다.

      그렇다면 싱글턴 패턴을 통해 new 키워드로 객체를 생성해도 동일한 객체가 반환된다는 것이며 정보 공유가 가능해집니다.

      그럼 어떻게 싱글턴 패턴을 구현할 것인가?

      중요한 포인트겠죠?

      흥미롭게도 싱글턴에서는 객체 생성을 new 키워드를 활용하지 않습니다.

      왜냐면 클래스의 생성자 접근 제한때문에 new 키워드를 방해하기 때문입니다.​

      그러면 객체 생성은 어떻게 할 수 있을까요..?

      바로 객체를 생성할 수 있는 메서드를 추가해 new 키워드 대신 객체 생성 메서드 호출만으로 객체를 생성합니다.

      싱글턴은 또한 내부적으로 플라이워이트 패턴으로 인해 중복 생성을 방지한다고 합니다.


      PHP에서는 _construct 메서드로 생성자의 메서드 이름이 고정되어 있습니다.

      class Config
      {
      	public function _construct()
      	{
          	echo _CLASS_."가 생성이 되었습니다.\n";
          }
      }
      include "Config.php";
      $obj = new Config ;

      위의 예시처럼 new 키워드를 통해 객체 생성을 하는데 이때 클래스의 생성자가 실행되어 동작되는 것이 확인됩니다.

      예시를 살펴보시면 계속 나오는 키워드가 있을겁니다.

      ​바로 public이라는 게 있죠? 이는 클래스의 접근을 제어할 수 있는 속성 중 하나입니다.

      public( 모든 접근 가능), private( 내부적 접근만), protected( 상속된 경우) 이렇게 3가지가 있습니다.

      ​이 속성을 바꿔주어 싱글턴 패턴을 구현하는 것입니다.

      여기서 중요한 포인트는 바로 new 를 통해 객체 생성할 때, 한 개의 단일 객체만 생성이 되도록 어떻게 할거냐 ? 였습니다.

      바로 생성자를 private로 바꿔준다면 가능합니다!

      ​맨 처음에 객체 생성 방법에는 new 키워드 사용만 있다고 했었는데 예외적으로 객체를 생성하는 방법이 하나 더 있습니다.

      기존의 생성된 객체를 복제해 새로운 객체를 만드는 것이죠. PHP에서는 clone이라는 키워드로 복제하도록 합니다.

      clone 복제될 때는 _clone() 매직 메서드가 있습니다.

      생성자를 private로 바꾸면면 new 키워드로 객체 생성이 불가능한 상태로 변환되었습니다.

      그렇다면 싱글턴 패턴이 적용된 클래스 생성을 위해선 내부에 선언된 메서드를 호출해야 합니다.

      ​new 키워드 사용이 제한된 상태에서 싱글턴 클래스의 객체를 만들기 위해서는 getInstance() 메서드를 확인합니다.

      getInstance는 자기 자신의 클래스를 객체로 생성 반환함

      class Config
      {
      	private function _construct()
          {
          	echo _CLASS_."가 생성이 되었습니다.\n";
              
          }
          private function _clone()
          {
          	echo _CLASS_."가 복제되었습니다.\n";
              
          }
          public static function getInstance()
          {
          	echo _CLASS_."객체를 생성합니다.\n";
              return new self();
          }
       
      }

      getInstance는 정적 메서드로, 객체가 없어도 메서드를 호출할 수 있습니다.

      근데 마지막 코드를 보면 new 키워드가 사용이 되었습니다.

      즉, 완벽한 싱글턴 패턴은 객체 생성을 외부의 접근 권한만으로 제한할 수 없다는 걸 의미합니다.

      또한 new 키워드를 썼다는 것은 다중 객체 생성이 가능하다는 것을 의미하게 됩니다.

      그럼 하나의 객체 생성을 위해서 참조체를 도입해야 합니다.

      참조체는 조건문을 활용해 참조체 변수에 값이 존재하면 새로운 객체를 생성하지 않고 저장된 객체를 반환하는 과정입니다.

      싱글턴 패턴은 뒷장에서 다룰 플라이웨이트 패턴의 간단한 형태입니다.

      싱글턴은 참조체에 객체를 저장한 후 if 문을 활용해 참조체의 객체 존재 여부를 검사하고 그에 따라 단일 객체 생성이 되도록 하는 것입니다.

      그러나 플라이웨이트 패턴의 참조체를 같이 적용하는 것은 코드의 양을 증가시키는 단점이 됩니다.

      싱글턴 확장

      싱글턴도 클래스이지만 생성자가 private로 선언되었기에 선언된 클래스로는 객체 생성과 확장이 불가능합니다.

      ​그러면 위에서 언급했던 마지막 속성인 protected를 활용한다면 상속이 가능하겠죠?

      ​싱글턴은 하나의 객체만 생성하는 패턴이지만 특수한 환경에서는 단일 객체 생성을 보장하지 못합니다.

      또한 싱글턴으로 생성한 객체는 공유자원으로 프로그램이 종료될 때까지 생존하게 됩니다.

      즉, 메모리에 계속 존재하기에 다른 설계자들은 안티 패턴으로 분류하기도 한다고 합니다.

      ​그러나 싱글턴은 패턴 카탈로그에서 4대 패턴에 들어가는 인기 패턴입니다.


      ⚙️ 메서드 패턴

      이는 팩토리 패턴의 확장 패턴이라고 보시면 됩니다. 팩토리 패턴에 추상화를 결합해 객체의 생성과 사용을 분리합니다.

      팩토리 패턴은 선언된 클래스의 객체를 직접 코드로 생성하지 않고 별도로 준비한 추상 메서드에 생성을 위임합니다.

      여기서 포인트는 바로 추상이라는 개념입니다. 복잡하고 어려운 기능을 한 눈에 파악하기는 어렵기에 간략하게 추상화해 이해를 쉽게 하는 것입니다.

      즉 추상화 == 코드 요약입니다. 즉 추상화를 위해선 요약된 정보와 실제 구현부를 분리합니다.

      ​php 언어안에서는 class 키워드 앞에 abstract 키워드를 붙여 추상 클래스를 작성합니다.

      abstract class Factory 
      {
      ...
      }

      위의 예시처럼 선언해주면 됩니다. 중요한 부분만 분리해 추상 클래스의 골격을 형성합니다.


      여기서 간략히 앞의 팩토리 패턴에 대해 설명하겠습니다.

      팩토리 패턴은 선언된 클래스 사용을 위해 객체 생성을 합니다. 객체 생성 요청은 별도의 클래스로 분리해 진행해야 합니다.

      require "factory.php" ;

      require "LgProduct. php" ;

      $fac = new Factory ;

      $pro = $fac -> create() ;

      $pro -> name() ;

      만든 팩토리 패턴에 추상화를 결합하고 패턴을 확장해 구현해야 합니다.

      추상화를 적용하게 된다면 팩토리 패턴은 팩토리 매서드 패턴과 추상 팩토리 패턴 2가지 형태로 구분됩니다.

      기존의 factory 클래스를 추상 클래스 (앞에 abstract를 붙임)로 변경하는데, 이는 객체 생산하고 사용하는 것을 분리하기 위함입니다.

      ​근데 여기서 추상 메서드도 선언이 되는데 추상화된 메서드를 가진 클래스만으로는 독립적인 객체 생성이 불가능하다고 합니다.

      ​그래서 객체 생성을 위해서는 추상 클래스를 상속받은(protected) 하위 클래스를 만들어 실제적인 추상 메서드의 구현부를 작성해야 합니다.

      이처럼 추상화는 선언과 구현이 분리됩니다. 그러나 추상화로 분리된 클래스 사이에는 의존성이 발생합니다.​

      선언된 추상 메서드는 상속받은 하위 클래스에서 반드시 메서드를 구현해야 합니다. 이로 인해 실제적인 동작 처리를 위임합니다.

      그로 인해 추상 클래스 안에서 미리 위임된 메서드 사용이 가능하게 됩니다.

      ​즉 추상화를 적용한 예제에서는 실제 객체 생성을 createProduct() 메서드로 위합니다.

      그러나 여기서는 선언만 되어있을 뿐, 아직 구현 코드가 되어 있지는 않습니다.

      (아직 구현이 되지 않아도 미리 메서드를 호출해 사용할 수 있다는 것을 알 수 있겠죠! )

      팩토리 메서드 패턴을 간단히 말하면 상위 클래스에서 정의를 결정하고 하위에서 구체적인 처리를 위임한다는 것입니다.

      추상화는 클래스가 추상 클래스와 구현 클래스로 분리가 됩니다.

      분리된 두 클래스는 상속을 통해 결합이 됩니다.

      ​여기서 중요한 포인트, 추상 클래스는 행위 메서드를 추상 클래스(상위 클래스)에서 구현이 가능하고 이를 하위 클래스에서 사용 가능합니다.

      구현 클래스(하위 클래스)에서는 선언된 메서드의 실제 구현부를 반드시 만들어야 합니다.

      즉 하위 클래스에 구현을 위임한다라고 표현을 할 수 있습니다.


      그러면 이제 하위 클래스에 상속하는 방법을 예제를 통해 알아보겠습니다.

      class ProductFactory extends Factory
      {
               public function _construct()
              {
       
                       echo _CLASS_."를 생성합니다.\n";
               }
      }

      여기서 추상 클래스는 extends를 통해 상속받습니다.


      하위 클래스 구현에서는 상위 클래스에서 선언한 형태와 모양이 일치해야 합니다.

      그래서 하위 클래스는 추상 클래스의 메서드를 오버라이딩합니다.

      또한, 팩토리 메서드 패턴은 new 키워드를 직접 사용해 객체 생성을 하지 않고 추상화로 재구현된 메서드를 호출해 객체 생성을 처리합니다.

      이로 인해 느슨한 코드를 작성할 수 있게 됩니다.

      정리하자면, 템플릿화된( 자주 사용하는 모습 본뜸) 구조적 골격은 다앙한 생성 패턴에 응용할 수 있습니다.

      다양한 하위 클래스를 구현할 수 있습니다.

      생성 패턴은 객체 생성 과정을 외부에 공개하지 않기 위해 사용하고 생성 패턴을 사용하면 new 키워드를 사용하는 빈도가 줄어듭니다.

      너무 어려운 단어를 사용하면 이해가 잘 안 되니 예제를 사용해보겠습니다.

      ​부장이 직원 한 명을 지정해 일을 직접 시키는 것이 아닌, 중간에 위치한 과장에 일을 위임하는 것이 팩토리 메서드 패턴입니다.

      ​대형 프로그램을 개발하다 보면 수많은 객체 관계를 설정해 객체를 생성해야 하는데 팩토리 메서드 패턴을 활용해 객체 생성을 따로 관리해 유지 보수와 수정이 쉬워집니다.

      ​마지막으로 정리를 하자면 구조화를 통해 추상과 구현 클래스로 분리합니다.

      또한 객체 생성하는 클래스를 따로 빼두어 캡슐화하면 응용 프로그램에 클래스가 종속되지 않도록 관리할 수 있습니다.

      구조패턴

      구조패턴은 객체와 클래스를 확장시키는 방법입니다.

      확장

      클래스는 하나의 책임. 역할로 설계됩니다. 하지만 하나의 책임으로는 전체의 기능을 구현 할 수 없습니다.

      단일 책임의 원칙은 클래스의 행동을 분리하는 설계원칙입니다.

      여러개로 분리된 책임과 행동은 서로 관계를 맺고 주어진 문제를 해결합니다.

      객체지향 개발은 클래스를 결합하여 더 큰 객체로 확장 합니다.

      구조패턴은 큰 구조의 객체를 형성하고관리하는 방법에 대해 설명합니다.

      객체를 확장하기위해 클래스의 상속과 합성을 사용합니다.

      상속

      상속은 객체지향 초창기시절부터 나온 개념입니다. 초기에는 객체를 확장하기위해 상속을 이용했습니다.

      상속은 객체지향 고유의 특징입니다. 상속은 정적인 결합을 통해 객체를 확장합니다.

      하지만 정적인 상속 결합은 대형 프로그램개발의 유연한 작업엔 한계가 있습니다.

      합성

      상속 결합의 단점을 보완하기 위한 기법으로 객체 합성을 사용합니다.

      합성이란, 하나의 객체가 다른객체에 포함하는 것을 말합니다.

      합성은 객체의 강한 의존 관계를 줄이고 유연한 객체 구조로 변결할 수 있으며, 실행 중에도 객체를 확장 할 수 있습니다.

      합성을 이용한 객체 확장은, 자원을 보다 효율적으로 사용할 수 있게 합니다.

      구조패턴은 상속보다 합성을 통해 다양한 객체 확장 방법을 제안합니다.


      ⚙️ 어댑터 패턴

      어댑터 패턴은 코드를 재사용하기위해 구조를 변경하는 패턴입니다.


      어댑터

      어댑터 패턴은 다른 말로 래퍼 패턴(wrapper pattern)이라고 합니다.

      래퍼는 감싸다는 의미로, 기존의 클래스를 새로운 클래스로 감싸는 기법 입니다. 래퍼 처리로 기존의 기능은 유지하면서 변경된 추가 코드를 삽입합니다.

      래퍼 처리된 객체를 어댑터라고 합니다.


      직접 사진찍은건데 좀 짜그라졌네요.

      • 어댑터 : 변환을 처리하는 객체

      • 어댑티 : 변환을 받아 사용하는 객체


      어댑터 종류

      어댑터는 다른 객체의 구조를 내가 원하는 인터페이스 방식으로 개선합니다. 구조를 개선하는 방법은 클래스의 상속을 이용하는 방법과 구성을 이용하는 방법 2가지 입니다.

      • 클래스 어댑터 : 상속

      • 객체 어댑터 : 구성

      클래스 어댑터

      클래스 어댑터는 클래스의 상속 특성을 이용하며, 클래스 어댑터를 사용하기 위해서는 다중 상속이 필요합니다.

      다중 상속이란 하나의 클래스가 2개 이상의 클래스에서 상속되는것을 말 합니다.

      클래스 어댑터는 2개의 클래스를 받아 기존 클래스의 메서드를 다른 메서드로 대체하는 방법입니다.


      근데.. 그거 아십니까 최신 프로그래밍 언어는 다중 상속을 지원하지 않습니다. php와 java또한 단일상속만 지원합니다.

      다중 상속에서는 클래스 메서드의 충돌이 발생합니다.

      아무래도 최신 언어로 다중상속을 이용하여 클래스 어댑터를 표현하기는 어렵겠습니다.

      그러나 클래스 어댑터는 별도의 어댑티를 만들지 않으며, 하나의 클래스로 어댑티 객체를 처리할 수 있다는 장접이 있습니다.

      객체 어댑터

      객체 어댑터는 객체의 의존성을 이용해 문제를 해결합니다.

      객체 어댑터는 기존 타깃인 객체의 인터페이스에 영향을 받으며, 타깃의 인터페이스가 복잡할수록 많은 작업이 필요합니다.

      객체 어댑터는 내부적으로 객체를 재구성합니다. 구성을 위해 기존 객체는 복합 객체로 변환됩니다.


      어댑터는 인터페이스를 변경합니다.

      하지만 어댑터는 인터페이스로 캡술화 됐기 때문에 클라이언트에서 변화를 눈치채지 못한채 그대로 사용할 수 있습니다.

      어댑터 패턴은 객체의 호환성을 개선하기 위한 기능들로 새롭게 합성합니다.

      객체를 구성으로 결합하면 느슨한 연결 방식으로, 보다 많은 유연성을 확보할 수 있다는 장점이 있습니다.

      그리고 구성은 프로그램 실행 도중에도 객체를 변경할 수 있습니다.


      예제

      기존 코드

      <php 
      //원본소스 
      class Math
      {
      	//입력한 값이 2배 증가합니다.
      	// 입력값과 반환값은 float형입니다.
      	public function twoTime (float $num): float
      	{
      		echo "실수 2배 적용합니다. In"
      		return $num*2;
      	}
      	//입력한 값이 절반으로 감소합니다.
      	// 입력값과 반환값은 float형입니다.
      	public function halfTime(float $num): float
      	{
      	echo "실수 1/2배 적용합니다. In";
      	return $num/2;
          }
      }

      곱셈을 계산하는 메서드 입니다. 매개변수 인자로 실수값을 받습니다.

      만약 인자값이 정수로 변경된다고 가정하면, 더이상 이 Math 클래스를 사용할 수 없습니다. 우리는 어댑터 패턴으로 이 문제를 해결할거에요.

      어댑터 제작

      <?php
      // 어댑터 인터페이스
      interface Adapter
      {
      	public function twice0f(int $num):int; 
          public function half0f(int $num):int;
      }

      인터페이스에서 입력한 값을 보정합니다. 정수 타입으로 지정한 후, 알맞는 구체 클래스를 만듭니다.

      +) 인터페이스 변경을 담당하는 클래스를 플러그블 어댑터라고 합니다.

      구현 코드

      <?php
      //새롭게 구현된 코드
      class Objects implements Adapter
      {
      	private $_adapter;
      	
          function _construct()
      	{
      	//기존 클래스의 객체를 생성합니다.
      	$this->_adapter = new math;
      	}
      
      	public function twice0f (int $num):int
      	{
      		echo "정수 2배 적용합니다. n";
      		//캐스팅을 통해 실수로 변환하여 전달합니다.
      		$_num = $this->_adapter->twoTime( (float)$num );
      		//캐스팅을 통해 정수로 변환하여 반환합니다.
      		return (int)$_num;
      	}
      
      	public function half0f(int $num): int
      	{
      		echo "정수 1/2배 적용합니다. \n";
      		
              //캐스팅을 통해 실수로 변환하여 전달합니다.
      		$num = $this->_adapter->halfTime( (float)$num );
      		//캐스팅을 통해 정수로 변환하여 반환합니다.
      		return (int)$_num;
      	}
      }

      작성한 어댑터는 기존의 Math 클래스를 상속받지 않습니다. 그 대신 객체의 생성자에서 기존 Math 클래스의 객체를 생성합니다.

      어댑터는 생성자에서 새로운 객체를 생성, 포함함으로 복합객체 입니다.

      입력된 정숫값을 실숫값으로 캐스팅하고, 기존 Math클래스의 메서드를 호출합니다. 결과값도 캐스팅해서 정수값으로 변경한 후 반환됩니다.

      클라이언트 코드

      ‹?php 
      include "Math.php";
      include "Adapter.php";
      include "Object.php";
      
      sobi = new Objects;
      
      //어댑터를 이용하여 두 배 계산합니다.
      echo $obj->twice0f(5);
      echo "\n";
      
      //어댑터를 이용하여 절반을 계산합니다.
      echo $obj-›half0f(4);

      이제 실행해볼까요.


      이처럼 어댑터 패턴을 이용하면 클래스와 메서드를 수정하지 않고, 원하는 형태로 변경 할 수 있습니다.

      어댑터 패턴은 한국에서 사용되는 220V 가전제품을 일본에서 110V에 연결하기 위해 변환 어댑터와 같은 컨버터를 사용하는 것으로 비유할 수 있습니다.

      행동패턴

      객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴입니다.

      한 객체가 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하며 객체 사이의 결합도 최소화에 중점을 둡니다.

      패턴을 주로 클래스에 적용하는지 아니면 객체에 적용하는지에 따라 구분되는 패턴입니다.


      ⚙️ 반복자 패턴

      내부 구조를 노출하지 않고, 집합체를 통해 원소 객체에 순차적으로 접근할 수 있는 방법입니다.

      우선, 집합과 배열에 대한 개념이 필요합니다.

      집합

      여러 개의 데이터를 하나로 묶은 것

      • 묶인 데이터 하나 → 요소

      • 반복자 패턴에서는 객체로 묶인 요소들을 관리하고 실행한다

      • 반복자 패턴에서는 객체를 배열로 묶어 관리 한다.

        그에 따른 예제는 아래와 같다

      $fruit = [
      	new Fruit("Apple"),
      	new Fruit("Banana"),
      	new Fruit("Berry"),
      	new Fruit("Grape"),
      ];
      #인스턴스화를 통해 객체를 생성함과 동시에 객체를 배열로 묶음 

      집합체 (Collection)

      객체의 모음

      반복자 패턴은 별도의 컬렉션 객체를 생성합니다.

      • 효율성!

      • 컬렉션 메서드를 통해 객체 생성과 삭제가 쉽다

      반복자

      iterator 메서드를 사용해 외부의 반복자 객체를 사용합니다!

      • isNext() 메서드 : 다음 객체의 존재를 확인합니다.

      • next() 메서드 : 다음 객체를 반환합니다.

      반복 객체는 컬렉션에 저장된 객체를 순환하도록 객체를 반환하는 역할입니다.

      마지막 객체 여부를 확인하는 작업이 필요함 → 이를 대체하기 위해 마지막 객체의 값으로 null을 넣어두기도 합니다.

      반복 로직과 반복자 패턴의 특징

      반복되는 처리를 분리하여 구현하기 위함

      • 실제 구현된 객체에 의존하지 않음

      • 독립적인 동작 유지를 위해 객체의 내부 메서드에 직접 접근하지 않음

      • 반복자의 메서드를 호출하여 처리함

      실습

      class Collectikon implements Aggregate
      {
      	private $objs = [];
      	private $last = 0;
      
      	public function getObj($id)
      	{
      		return $this->objs[$id];
      	}
      
      	public function getLast()
      	{
      		return $this->last;
      	}
      
      	public function append($obj)
      	{
      		array_push($this->objs, $obj);
      		$this->last++;
      	}
      		
      	public function iterator()
      	{
      		return new IteratorObject($this);
      	}
      
      }
      //반복자 객체 호출 
      $loop = $menu->Iterator();
      while ($loop->isNext())
      {
      	$item = $loop->next();
      	echo $item->getName()."\n";
      }

      반복자 패턴의 관련 패턴

      • 방문자 패턴

      • 팩토리 메서드

      • 복합체 패턴


      ⚙️방문자 패턴

      반복자 패턴은 집합체 요소의 개수를 파악하고, 요소의 개수에 접근하여 함께 처리합니다.

      공통된 객체의 데이터 구조와 처리를 분리하는 패턴이에요.

      비슷한 종류의 객체들을 가진 그룹에서 작업을 수행해야 할 때 주로 사용되는 패턴입니다.

      객체의 관계와 메시지 처리

      메시지는 객체 간 행위를 호출하는 객체입니다.

      기존 객체에서 행위동작을 분리하고 새로운 행위를 추가할 수 있도록 하기 위해 사용합니다.

      기존 객체를 직접 수정하기 어려울 경우 방문자 패턴을 통해 대신 처리할 수 있습니다.

      많은 연산으로 클래스를 더럽히고 싶지 않을 때 사용합니다.

      캡슐화

      데이터와 함수를 하나의 그룹으로 묶어 처리

      = 번들링

      정보를 은닉하기 위함

      ⇒ 캡슐화 된 객체에 데이터를 처리하기 위한 동작을 추가하고 싶은 경우

      (e.g. 상품 계산 클래스에 가격의 부가가치세를 계산하는 메서드)

      → 데이터를 갖고 있는 클래스에 클래스 선언을 수정합니다.

      분리

      분산된 객체에서 공통된 처리 로직만 분리합니다.

      공통된 로직 구조를 별도의 객체로 분리합니다.

      방문자 패턴은 개방-폐쇠 원칙을 적용한다.

      [확장에 대해 열려있다.]

      클래스를 설계할 때, 특별한 이유가 없는 한 확장을 금지해서는 안된다.

      [수정에 대해 닫혀있다.]

      확장을 할 때 마다 기존의 클래스를 수정하면 안된다.


      ⚙️ 상태 패턴

      상태 패턴은 조건에 따른 별개의 동작을 캡슐화하여 독립된 동작으로 구분하는 패턴입니다.

      때문에 상태 패턴은 상태 표현 객체라고도 불립니다.

      상태값은 처리 로직을 구별할 수 있는 특정한 값으로 조건 비교, if문을 사용하여 동작을 분리합니다.

      모든 상태에 대해 객체를 생성하고 관리하는데, 새로운 상태 객체를 생성하는 것은 시스템의 메모리 자원을 할당하는 일입니다.

      <?php
      
      const OPEN = 0x01; //주문
      const PAY = 0x02; //결제중
      const ORDERED 0x04; //주문완료
      
      $state = NULL;
      $state = OPEN;
      
      if ($state == OPEN) {
           echo “주문\n”;
      } else if ($state == PAY) {
          echo “결제중\n”;
      } else if ($state == ORDERED) {
          echo “주문완료\n”;
      }

      여기서 if문을 switch문으로 바꾸면 더 효율적이고 높은 성능을 발휘할 수 있습니다.

      그런데 제어문은 규모가 커질수록 코드의 흐름을 이해하고 전체 동작을 파악하는데 많은 시간이 필요하다는 단점이 있습니다.

      이럴 때 가변함수를 이용하면 보다 명확히 구분할 수 있습니다.

      <?php
      $state = “ordered”;
      if($state && function_exists($state)){
         $state();
      }
      
      // 오픈상태
      function open()
      {
      	echo “주문\n”;
      }
      
      //결제상태
      function pay()
      {
      	echo “결제중\n”;
      }
      
      //주문상태
      function ordered()
      {
      	echo “주문완료\n”;
      }
      
      //완료상태
      function finish()
      {
      	echo “처리완료\n”;
      }

      객체 형태로 상태를 분리할 경우 상태의 동작에 객체를 위임할 수 있습니다.

      때문에 객체의 상태값과 직접적인 객체의 상태값에 영향을 받습니다.

      각 상태의 동작은 서브 클래스 형태로 캡슐화할 때 코드가 독립적으로 수행할 수 있도록 해야 합니다.

      이렇게 하면 각 클래스는 상태의 변화에 대응하며 독립적으로 실행 가능합니다.

      서브 클래스 생성시 인터페이스를 적용하는데, 이 인터페이스는 서브 클래스의 통일성을 유지하기 위해 사용합니다.

      한편, 각각의 상태를 객체로 캡슐화하다보면 클래스 파일이 늘어난다는 단점이 있습니다.

      <?php
      Class JinyOrder
      {
         private $state;
      
          //객체 초기화
          public function_construct()
          {
          //상태 서브 클래스 객체의 인스턴스를 생성합니다.
            $this->state = [
          	‘ORDER’ => new StateOrder(),
          	‘PAY’ => new StatePay(),
          	‘ORDERED’ => new StateOrdered(),
          	‘FINISH’ => new StateFinish()
          ];
          }
          
          //상태의 서브 클래스를 호출합니다.
          public function process($status)
          {
          	$this -> state[$status]->process();
          }
      }

      초기화 과정에서 상태별로 클래스의 객체를 생성하는데 각 상태에 따라 여러 변수를 추가로 사용하는 것이 단점이라고 할 수 있습니다.

      상태 패턴은 상태값에 따라 실제 동작되는 상태 객체를 결정하고 호출하는데 상태 전이를 통해 관리할 수 있습니다.

      상태 전이(state transition)은 동작에 따라 변하는 상태값으로 특정한 규칙에 의해 변동되며, 상태 패턴이 바로 이 상태 전이를 명확히 표현합니다.

      상태 전환은 코드의 흐름에 따라 상태를 처리하며 현재 자신의 상태를 유지하는 것으로 상태를 공유할 때 정적 변수를 응용하여 표현할 수 있습니다.

      상태패턴의 효과

      • 조건문 해결 : 조건 분기 없이 상태값을 이용용

      • 런타임 : 상태별로 위임 객체를 변경

      • 확장성 : 동작을 상태 객체에 위임하여 확장

      • 변화 : 변경된 상태에 따라 행동을 위임임


      ⚙️ 메멘토 패턴

      메멘토 패턴은 객체의 상태를 저장하여 이전 상태로 복구하는 패턴으로 SolverState로 객체의 상태를 관리합니다.

      객체의 상태를 저장하고, 저장된 상태의 객체를 복원합니다.

      객체가 동작 전의 상태로 만드는 방법

      1. 객체를 실행하기 전에 동작을 역순으로 처리하는 로직을 다시 작성하는 방법

      2. 객체의 동작을 되돌리기 위해 실행 전의 객체를 통째로 저장하는 방법입니다.

      두번째 방법이 첫번째 방법에 비해 수월한 편입니다.

      메멘토 패턴의 특징

      메멘토 패턴은 캡슐화를 파괴하지 않고 객체 상태를 저장합니다.

      객체 저장은 스택 구조 배열을 사용합니다.

      객체 저장과 복원 위한 인터페이스

      • 원조본(originator)

      • 케어테이커(caretaker)

      구현

      클래스 생성

      <?php
      class Memento
      {
          //객체를 저장
          protected $obj;
          
          //원조본(Originator)에 의해 생성
          Public function _construct($obj)
          {
          
          //객체를 복제
              $this->obj = clone $obj;
          }
          
          //저장된 객체 읽기
          public function getObject()
          {
          return $this->obj;
          }
      }
      • 클래스를 protected 속성 사용하여 저장

      • 상속 클래스 내부에 매서드 생성

      • 객체를 저장, 복원을 위한 Originator 클래스 생성

      <?php
      Class Originator
      {
          protected $state;
          
          public function create()
          {
          	echo “>메멘토 객체를 생성합니다. \n”;
          	return new Memento($this->state);
          }
          
          public function restore($memento)
          {
          	echo “>메멘토 객체로 복원합니다.\n”;
          	$this->state = clone $memento->getObject();
          }
          
          public function getState()
          {
          	return $this->state;
          }
          public function getState()
          {
          	return $this->state;
          }
          
          public function setState($state)
          {
          	$this->state = $state;
          }
      
      }

      *Originator 클래스는 객체를 복원 또는 저장하기 위한 프로피터 하나 가지고 있음.

      케어테이커(caretaker)는 실행 취소 메커니즘이고, 제한적 범위의 인터페이스를 가집니다. 또한, 다수의 메멘토를 보관하고, 관리합니다.

      <?php
      class CareTaker
      {
          	protected $stack;
          
          	public function _construct()
          {
          	$this->stack = array();
          }
          
          public function push($origin)
          {
          	$memento-> $origin->create();
          
          	array_push($this->stack, $memento);
          }
          
          public function undo($origin)
          {
          	$memento = array_pop($this->stack);
          
          	$origin->restore($memento);
          
          	return $origin->getState();
          }
      }

      또한, CareTaker클래스는 메멘토 객체를 스택 구조로 저장하며 복원시 스택에서 메멘토를 가져옵니다.

      메멘토 패턴과 같이 활용하는 패턴

      • 명령 패턴

      • 프로토타입 패턴

      • 상태 패턴

      • 반복자 패턴

      Outro

      지금까지 쉽게 배워 바로 써먹는 디자인 패턴으로 다독다독을 진행한 Dev-River 이었습니다.

      24가지 패턴을 전부 기록하지는 못했지만, 서로 패턴에 대해 알아보고 스터디를 하는 과정에서 배워가는것이 많았습니다.

      글이 많이 길어졌네요. 감사합니다.


      각각의 조원이 학습한 기록들을 첨부합니다.

      • 박서진 (생성패턴) : https://velog.io/@szzjinee/다독다독-쉽게-배워-바로-써먹는-디자인-패턴

      • 권민기 (구조패턴) : https://velog.io/@min_gi1123/series/디자인패턴-쉽게-배워-바로-써먹는-디자인-패턴

      • 이지수 (행동패턴) : https://velog.io/@windylung/디자인패턴반복자패턴-방문자패턴

      • 김주희 (행동패턴) : https://juunnystudio.com/76

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      min_gi1123 님의 최신 블로그

      더보기
      동영상 기고하기