[OCA] 파워플랫폼 챌린저스 - 1주차 과제

오경찬·2022년 7월 30일
0

Fluent API(fluent interface)

  • 메소드 체이닝에 상당 부분 기반한 객체 지향 API 설계 메소드
    • 메소드 체이닝(Method)
      OOP에서 여러 메소드를 이어서 호출하는 문법
      메소드가 객체(this)를 반환하여 여러 메소드를 순차적으로 선언할 수 있도록 한다.
      모든 메서드를 함께 연결하여 단일 명령문을 형성할 수 있는 일반적인 기술이다.
  • 소스 코드의 가독성을 산문과 유사하게 만드는 것이 목적
    → 즉 개발자들이 읽기 편하고, 라인 줄 줄이고, 간단하게 기술할 수 있다는 매력을 갖고 있다.

  • 특히 인터페이스 안에 도메인 특화 언어(DSL)를 작성

var translations = new Dictionary<string, string>
                   {
                       {"cat", "chat"},
                       {"dog", "chien"},
                       {"fish", "poisson"},
                       {"bird", "oiseau"}
                   };

// Find translations for English words containing the letter "a",
// sorted by length and displayed in uppercase
IEnumerable<string> query = translations
	.Where   (t => t.Key.Contains("a")) // Key 값에 a가 있는지 확인
	.OrderBy (t => t.Value.Length) // Value 값의 길이 오름차순
	.Select  (t => t.Value.ToUpper()); // Value 값을 대문자로 

// The same query constructed progressively:
var filtered   = translations.Where (t => t.Key.Contains("a"));
var sorted     = filtered.OrderBy   (t => t.Value.Length);
var finalQuery = sorted.Select      (t => t.Value.ToUpper());

인터페이스와 클래스 구현(일반적인 코드 vs fluent API 구현한 코드)

  • 인터페이스를 구현하고 기술할 메소드를 설계
  • 클래스에서 인터페이스를 상속받아 내부 변수 및 프로퍼티, 메소드를 구현

  • 인터페이스는 반환이 void가 아닌 인터페이스 명을 기술
  • 상속받은 클래스 또한 void가 아닌 인터페이스로 반환 처리 → return this를 통해 자신을 반환
    • static 으로 New() 생성자를 구현하여 객체 인스턴스를 반환

인스턴스 생성

  • 클래스의 인스턴스를 생성하여 객체 생성 후 각 메소드를 이용해 지역변수에 값 할당

  • static으로 생성자를 호출하는 New()를 구현하여 인스턴스를 생성한 다음 세미콜론(;)으로 구문을 닫지 않고 바로 점(.)을 통해 간결하게 값을 할당할 수 있음!

Fluent Interface Design Pattern을 언제 사용해야 하나요?

  1. 개발자가 본격적인 프로그래머가 아닌 경우 UNIT 테스트 중.
  2. 코드가 비즈니스 로직에 만족하는지 여부를 이해할 수 있도록 프로그래머가 아닌 사람들이 코드를 읽을 수 있기를 원할 때.
  3. 당신이 구성 요소 판매자이고 인터페이스를 더 단순하게 만들어 다른 사람들과 비교하여 시장에서 눈에 띄기를 원하는 경우.

DSL

사전 개념

  • DDD (도메인 주도 개발, Domain-Driven Development) 방법론
    : 도메인(실세계에서 사건이 발생하는 집합 / 여기서는 비즈니스 Domain으로 유사한 업무의 집합을 의미) 중심으로 설계해 나가는 것
    : 기존의 어플리케이션 설계가 비즈니스 Domain에 대한 이해가 부족한 상태에서 설계 및 개발되었다는 반성에서 출발. → 데이터(객체) 주도 설계 탈피!
    : DDD에서는 기존의 현업에서 IT로의 일방향 소통구조를 탈피하여 현업과 IT의 쌍방향 커뮤니케이션을 매우 중요하게 생각
    • 옷 쇼핑몰에서의 DDD Example
      손님들이 주문하는 도메인(Order Domain) / 점주입장에서 옷들을 관리하는 도메인(Manage Domain) / 쇼핑몰 입장에서 월세, 관리비 등 건물에 대한 관리를 담당하는 도메인(Building Domain)


같은 객체가 여러 개 존재할 수 있다. 이때 Bounded Context에 따라 객체(Model)의 역할은 완벽히 바뀐다. → 서로 다른 도메인 영역에 영향을 끼치려면 API 호출을 통해서 이루어져야..

EX) 주문 도메인의 옷 == 손님들에게 팔기 위한 객체 정보 ↔ 옷을 관리하는 도메인의 옷 == 점주입장에서 관리하기 위한 객체 정보

각 도메인은 서비스 별로 철저히 분리 → 어플리케이션 또는 그 안의 모듈간의 의존성은 최소화하고, 응집성은 최대화 → 변경과 확장에 용이한 설계

  1. Application Layer: 주로 도메인과 Repository를 바탕으로 실제 서비스(API)를 제공하는 계층
  2. Domain Layer: Entity, VO(Value Object)를 활용하여 도메인 로직이 진행되는 계층
  3. Infrastructure Layer: 쉽게 말하면 외부와의 통신(ORM, DB, NoSql)을 담당하는 계층

그래서 DSL(Domain-specific language)??

  • 특정 도메인을 적용하는데 특화된 컴퓨터 언어 → 즉 특정용도에서만 사용 가능

  • 특정 영역의 문제 해결에는 그 영역에 맞는 특화된 도구를 사용하자는 취지.

  • 표현 방식은 해당 도메인의 전문가(비 프로그래머)가 이해할 수 있는 형태여야 함 → 코드가 일반 자연어를 읽는 것과 같이 쉽게 이해됨

    라이브러리나 프레임워크등을
    도메인 Expert 나 우리같은 일반 프로그래머에게 조차도 사용하기 쉽게 (Fluent 하게) 제공해주는것 자체,
    제한된 상황 내에서 고민과 노력으로 이루어진 "깔끔한 문장" 으로 우리에게 제공하는 API 자체가 DSL

  • 우리 주변에 있는 DSL
    SQL, HTML, CSS , java . ANT, Maven, struts-config.xml, Seasar2 S2DAO, HQL(Hibernate Query Language), JMock- Ruby . Rails Validations, Rails ActiveRecord, Rake, RSpec, Capistrano, Cucumber

DSL의 종류

DSL은 내부 DSL과 외부 DSL로 구분할 수 있다.

1. 내부 DSL

  • 특수한 목적을 위해 제한된 방법으로 호스트 언어를 사용하는 방식, 자체적으로 의존하는 무언가를 만드는 경우에 해당

  • 임베디드 DSL, Fluent Interfaces 라고도 한다.

  • 내부 DSL에서는 API와 DSL의 경계가 모호해 비슷하게 생각하는 경향이 있음 → 좀 더 일반 사용자가 알아보기 쉬운 API가 내부 DSL로 생각해도 될 듯 함

  • 사용하던 도구를 그대로 이용할 수 있음, 처리 결과를 쉽게 예측할 수 있음
    EX) Ruby, Smalltalk 등

2. 외부 DSL

호스트 언어가 아닌 다른 언어에서 생성된 DSL이다.

  • 대부분 자체적인 문법을 가지지만 기존 언어의 문법을 쓰는 예도 있다.

  • 외부 DSL에서는 DSL과 범용 언어(GPL : General Purpose Language)과의 경계가 모호해지는 경향이 있다. 그 차이는 언어 작성자와 언어 사용자의 목적에 있다. 특정 영역에서 언어의 작성자가 아닌 사용자의 목적에 부합하는, 이해를 할 수 있으면 외부 DSL이다.

  1. XML DSL == 외부 DSL (xml이라는 언어에서 작성된 DSL)

  1. 위의 xml이 보기 불편해서 원하는 대로 문법 작성 → DSL (단순 string 집합체 이기 때문에 파싱하기 위한 파서[인터프리터 혹은 컴파일러 내부의 구문 분석하는 프로그램]를 만들어서 써야함)

→ 만약 이 파서가 내가 쓰는 언어 내에서 그대로 쓸 수 있다면? 내가 쓰는 언어의 파서가 알아서 구문 분석까지 해준다면? == 내부 DSL

  1. 현실적으로 내가 쓰는 언어 내에서 그대로 사용하는게 best지만 파써를 직접 구현하기보다 적당한 DSL을 쓰는 것이 좋다.
    → 2번의 코드를 루비 문법으로 작성한 코드 == 내부 DSL

  1. 누가 봐도 자바 코드 → 내가 원하는 문장 자체를 쓴 것은 아니지만 fluent하게 표현함 → 이것도 DSL

Fluent API를 이용한 DSL 작성

애저 펑션의 첫 엔트리 포인트에서 만나는 부분

public static IFunctionFactory Factory { get; set; } = new FunctionFactory(new AppModule());

[FunctionName(nameof(XmlToXmlMapperHttpTrigger))]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post", Route = "mappers/xml/xml")] HttpRequestMessage req,
    ILogger log)
{
    return result = await Factory.Create<IXmlToXmlMapperFunction, ILogger>(log)
                                 .InvokeAsync<HttpRequestMessage, HttpResponseMessage>(req)
                                 .ConfigureAwait(false);
}

  • Fluent API를 이용한 메소드 체이닝을 통해 위의 그림과 같은 워크플로우를 문자 그대로 자연스럽게(Fluently) 표현하고 있음
  • 일단 사용자가 애저 펑션을 호출하면 1) 서비스 로케이터 팩토리를 생성하고, 2) 팩토리는 펑션 인스턴스를 생성한 후, 3) 해당 펑션 인스턴스는 지정된 메소드를 실행시켜 결과값을 받아 반환하는 아주 간단한 워크플로우
    → 이 모든 것을 Fluent API를 이용한 메소드 체이닝을 통해 구현했다.
  • 메소드 이름 역시 Create, InvokeAsync 와 같이 상당히 직관적
  • 즉, 메소드 이름을 보고 곧바로 이 메소드가 어떤 역할을 하게 되는지 유추가 가능한 데, 이걸 거창하게 얘기하면 "유비쿼터스 언어를 이용해서 DSL을 작성한 것" 이라고도 볼 수 있다.
profile
코린이 입니당 :)

0개의 댓글