"클래스 대신 맵을 사용하여 데이터 표시"-Rich Hickey


19

에서 리치 키스 마크에 의해이 비디오 는 Clojure의 창조자, 그는 자바에서 수행으로, 대신 그것을 표현하는 클래스를 사용하는 데이터를 표현하기 위해지도를 사용하여 조언한다. API 사용자가 입력 맵이 단순히 맵으로 표시되는 경우 입력 키가 무엇인지 어떻게 알 수 있는지 더 잘 이해할 수 없습니다.

:

PersonAPI {
    Person addPerson(Person obj);
    Map<String, Object> addPerson(Map<String, Object> personMap);
}

두 번째 함수에서 API 사용자는 개인을 작성하기위한 입력이 무엇인지 어떻게 알 수 있습니까?



또한 이것을 알고 싶습니다. 예제 질문이 그 대답에 충분하지 않다고 생각합니다.
sydan

SE 어딘가에서이 토론을 본 것을 알고 있습니다. 나는 그것이 JavaScript와 관련이 있다고 생각하지만, 논쟁은 동일했습니다. 그래도 찾을 수 없습니다.
Sebastian Redl

2
Clojure는 Lisp이므로 Lisp에 적절한 작업을 수행해야합니다. Java를 사용할 때 Java로 코딩하십시오.
AK_

답변:


12

Exagg'itive Summary (TM)

몇 가지를 얻습니다.

  • 프로토 타입 상속 및 복제
  • 새로운 속성의 동적 추가
  • 동일한 클래스의 서로 다른 버전 (사양 수준)의 개체 공존
    • 최신 버전 (사양 수준)에 속하는 개체에는 추가 "선택적"속성이 있습니다.
  • 오래되고 새로운 속성의 내성
  • 유효성 검사 규칙의 내부 검사 (아래에 설명)

한 가지 치명적인 단점이 있습니다.

  • 컴파일러는 맞춤법이 틀린 문자열을 확인하지 않습니다.
  • 자동 리팩토링 도구는 멋진 키를 지불하지 않으면 속성 키 이름의 이름을 바꾸지 않습니다.

문제는, 음, 내성 검사를 사용하여 내성 검사를 할 수 있다는 것입니다 . 이것은 일반적으로 일어나는 일입니다.

  • 반사를 가능하게합니다.
  • 큰 내성 라이브러리를 프로젝트에 추가하십시오.
  • 속성 또는 주석으로 다양한 객체 메소드 및 속성을 표시하십시오.
  • 내성 라이브러리가 마법을 발휘하게하십시오.

다시 말해, FP와 인터페이스 할 필요가 없다면 Rich Hickey의 조언을 따를 필요가 없습니다.

String속성 키로 사용하는 것이 가장 간단 하지만 s 를 사용할 필요는 없습니다 String. Android ™를 포함한 많은 레거시 시스템은 전체 프레임 워크에서 정수 ID를 광범위하게 사용하여 클래스, 속성, 리소스 등을 나타냅니다.

Android는 Google Inc.의 상표입니다.


두 세상 모두 행복하게 만들 수도 있습니다.

Java 세계의 경우 평소와 같이 getter 및 setter를 구현하십시오.

FP 세계의 경우

  • Object getPropertyByName(String name)
  • void setPropertyByName(String name, Object value) throws IllegalPropertyChangeException
  • List<String> getPropertyNames()
  • Class<?> getPropertyValueClass(String name)

이 기능에는 못생긴 코드가 있지만 코드를 읽는 스마트 플러그인을 사용하여 IDE 플러그인이 있습니다.

사물의 Java 측면은 평소와 같이 성능이 뛰어납니다. 그들은 코드의 그 추악한 부분 을 사용하지 않을 것 입니다 . Javadoc에서 숨길 수도 있습니다.

세계의 FP 측은 원하는 "간단한"코드를 작성할 수 있으며 일반적으로 코드가 느리다는 소리를 내지 않습니다.


일반적으로 소프트웨어 개발에서 객체 대신 맵 (프로퍼티 백)을 사용하는 것이 일반적입니다. 함수형 프로그래밍이나 특정 유형의 언어에는 고유하지 않습니다. 주어진 언어에 대한 관용적 접근 방식은 아니지만 그것을 요구하는 상황이 있습니다.

특히, 직렬화 / 역 직렬화에는 종종 유사한 기술이 필요합니다.

"map as object"에 관한 일반적인 생각.

  1. 여전히 "객체와 같은 맵"을 검증하는 기능을 제공해야합니다. 차이점은 "객체로 매핑"을 통해보다 유연하고 (제한이 적은) 검증 기준을 허용한다는 것입니다.
  2. "개체로 매핑"에 추가 필드를 쉽게 추가 할 수 있습니다.
  3. 유효한 객체의 최소 요구 사항을 지정하려면 다음을 수행해야합니다.
    • 맵에 예상되는 "최소 필요한"키 세트를 나열하십시오.
    • 값을 검증해야하는 각 키에 대해 값 검증 기능을 제공하십시오.
    • 여러 키 값을 확인해야하는 유효성 검사 규칙이있는 경우 해당 규칙도 제공하십시오.
    • 장점은 무엇입니까? 이 방법으로 사양을 제공하는 것은 내성적입니다. 최소한의 필수 키 집합을 쿼리하고 각 키에 대한 유효성 검사 기능을 얻는 프로그램을 작성할 수 있습니다.
    • OOP에서, 이들 모두는 "캡슐화"라는 이름으로 블랙 박스에 롤업됩니다. 기계가 읽을 수있는 유효성 검사 논리 대신 호출자는 사람이 읽을 수있는 "API 설명서"만 읽을 수 있습니다 (다행히 존재하는 경우).

commonplace나에게는 조금 강해 보인다. 나는 그것이 당신이 묘사 한대로 사용된다는 것을 의미하지만, 라이브러리가 숨기려고 가장 악의적으로 시도하는 악명 높고 부패하기 쉬운 (예 : 바이트 배열 또는 베어 포인터) 중 하나이기도합니다.
Telastyn

@Telastyn이 "못생긴 뱀의 못생긴 머리"는 일반적으로 두 시스템 사이의 통신 경계에서 발생하는데, 어떤 이유로 통신 또는 프로세스 간 채널로 인해 객체를 그대로 텔레포트 할 수 없습니다. Protocol Buffers와 같은 새로운 기술이 객체로 사용되는이 고풍의 사용 사례를 거의 제거했다고 생각합니다. 다른 유효한 유스 케이스가 여전히 있을지 모르지만 나는 그것에 대해 거의 알지 못합니다.
rwong

2
치명적인 단점은 동의하십시오. 그러나 "맞춤법 오류"및 "리 팩터하기 어려운"속성 키 이름을 가능한 한 상수 또는 열거 형 으로 유지하면 문제가 해결됩니다. 물론, 그것은 일부 확장 성을 제한하지 않습니다 :-(.
user949300

"한 가지 치명적인 단점"이 실제로 치명적이라면 왜 일부 사람들이 효과적으로 사용할 수 있습니까? 또한 클래스와 정적 타이핑은 직교하므로 클로저에서 클래스를 동적으로 입력하더라도 정의 할 수 있습니다.
Nathan Davis

@NathanDavis (1) 내 대답은 정적 타이핑 관점 (C #)으로 작성되었음을 인정하고 나는 asker의 동일한 관점을 공유하기 때문에이 대답을 썼습니다. 나는 FP 중심의 관점이 부족하다는 것을 인정한다. (2) SE.SE에 오신 것을 환영합니다. Clojure에서 존경받는 인물이므로 기존 답변이 만족스럽지 않으면 시간을내어 답변을 작성하십시오. Downvotes는 평판을 빼고 새로운 답변은 평판을 빨리 끌어 올리는 Upvotes를 유치합니다. (3) "불완전한 개체"가 어떻게 유용한 지 알 수 있습니다. 주어진 개체 (이름, 아바타)에 대해 2 개의 속성을 쿼리하고 나머지는 생략 할 수 있습니다.
rwong

9

그것은 그가 말하는 것을 정말로 아는 누군가에 의한 훌륭한 이야기입니다. 나는 독자들이 전체를 볼 것을 권장합니다. 길이는 36 분입니다.

그의 주요 요점 중 하나는 단순성이 나중에 변화의 기회를 열어 준다는 것입니다. 클래스를 선택 Person하면 지적한 바와 같이 정적으로 검증 가능한 API를 생성 할 수있는 즉각적인 이점이 있지만, 기회를 제한하거나 나중에 변경 및 재사용 비용을 증가시키는 비용과 함께 제공됩니다.

그의 수업은 수업을 이용하는 것이 합리적인 선택 일 수 있지만 비용을 충분히 인식하는 의식적인 선택이어야하며 프로그래머는 전통적으로 비용을 고려하지 않고 이러한 비용을 알아내는 데 매우 열악한 일을합니다. 요구 사항이 증가함에 따라 해당 선택을 재평가해야합니다.

다음은 Person객체 목록을 사용하는 것보다 맵 목록을 사용하는 것이 잠재적으로 더 간단한 일부 코드 변경 (대화에서 언급 된 것 중 하나 또는 두 개)입니다 .

  • REST 서버로 사람 보내기 ( Map프리미티브를 전송 가능한 형식으로 만들기 위해 작성된 함수 는 재사용 성이 높으며 라이브러리에서도 제공 될 수 있습니다. Person오브젝트는 동일한 작업을 수행하기 위해 사용자 정의 코드가 필요할 수 있습니다).
  • 관계형 데이터베이스 쿼리에서 사람 목록을 자동으로 구성합니다. (다시 말해, 재사용이 가능한 하나의 일반적인 기능).
  • 사람을 표시하고 편집 할 양식을 자동으로 생성합니다.
  • 공통 기능을 사용하여 학생 대 직원과 같이 비 균일 한 개인 데이터로 작업하십시오.
  • 특정 우편 번호에 거주하는 모든 사람의 목록을 가져옵니다.
  • 해당 우편 번호를 사용하여 특정 우편 번호의 모든 업체 목록을 가져옵니다.
  • 다른 고객에게 영향을 미치지 않으면 서 개인에게 고객 별 필드를 추가하십시오.

우리는 이런 종류의 문제를 항상 해결하고 그에 대한 패턴과 도구를 가지고 있지만, 처음에 더 단순하고 유연한 데이터 표현을 선택하여 업무를 더 쉽게 할 수 있을지에 대해서는 거의 생각하지 않습니다.


이것에 대한 이름이 있습니까? 예를 들어, 객체 속성 매핑 또는 객체 속성 매핑 (ORM과 같은 줄에)?
rwong

4
Choosing a class to represent a Person provides the immediate benefit of creating a statically-verifiable API... but that comes with the cost of limiting opportunities or increasing costs for change and reuse later on.잘못되고 믿을 수 없을 정도로 불쾌합니다. 그것은 향상 당신이 파괴 변경을 할 때, 컴파일러가 자동으로 발견하고 당신을 위해 필요 속도에 전체 코드베이스를 가지고 업데이트 할 것을 모든 장소를 지적하기 때문에,에 나중에 변경을위한 기회를. 그것은 당신이 그것을 할 수없는 동적 코드에있어서, 당신은 정말로 이전 선택에 묶여 있습니다!
메이슨 휠러

4
@MasonWheeler : 당신이 말하는 것은 더 역동적 인 (그리고 더 느슨하게 형식화 된) 데이터 구조보다 컴파일 타임 타입 안전을 중요하게 생각한다는 것입니다.
Robert Harvey

1
다형성은 OOP로 제한되는 개념이 아닙니다. 지도의 경우 포괄적 다형성 (요소가지도가 처리 할 수있는 특정 유형의 하위 유형 인 경우) 또는 임시 다형성 (요소에 태그가 지정된 경우)이있을 수 있습니다. 내부입니다. 맵에서 수행 할 수있는 작업은 다형성 일 수도 있습니다. 디스패치 할 때 요소에 대해 고차 함수를 사용하거나 임시로 사용할 때의 파라 메트릭 다형성. 네임 스페이스 또는 다른 형태의 가시성 관리를 통해 캡슐화를 수행 할 수 있습니다. 근본적으로 객체의 격리는 데이터 유형에 작업을 할당하는 것과 같지 않습니다.
siefca

1
@GillBates 왜 그렇게 말합니까? 이러한 가상 메소드를 "맵 안에"넣을 수있는 기회를 잃어 버렸습니다. 그러나 이것이 바로 Rich Hickey가 말한 "ActiveObjects"는 실제로 반 패턴입니다. 데이터를 데이터 (데이터)로 취급해야하며 동작과 얽 히지 않아야합니다. 우려를 분리함으로써 달성 될 수있는 단순성 이점은 매우 크다.
Virgil

4
  • 데이터에 변화가있을 수있는 유연한 컨텐츠로 동작이 거의 없거나 전혀없는 경우 맵을 사용하십시오. N 필드, N setter 및 N getter 가있는 Anemic Domain Model 로 구성된 전형적인 "javabean"또는 "Data Object"인 IMO 는 시간 낭비입니다. 멋진 스 메시 클래스에 싸서 영광스러운 구조로 다른 사람들에게 깊은 인상을 주려고하지 마십시오. 정직하고 의도를 명확하게 하고지도를 사용하십시오. (또는 도메인, JSON 또는 XML 객체에 의미가있는 경우)

  • 데이터에 실제 동작이 중요한 경우에는 일명 메서드 ( Tell, Do n't Ask )가 있고 클래스를 사용하십시오. 실제 객체 지향 프로그래밍을 사용하기 위해 등을 두 드리십시오 :-).

  • 데이터에 필수 유효성 검사 동작과 필수 필드가 많으면 클래스를 사용하십시오.

  • 데이터에 적당한 양의 유효성 검사 동작이있는 경우 경계선이됩니다.

  • 데이터가 속성 변경 이벤트를 발생시키는 경우 실제로 Map을 사용하면 훨씬 쉽고 지루합니다. 작은 하위 클래스를 작성하십시오.

  • 지도 사용의 한 가지 주요 단점은 사용자가 값을 문자열, int, Foos 등으로 캐스팅해야한다는 것입니다. 이것이 성 가시고 오류가 발생하기 쉬운 클래스를 고려하십시오. 또는 관련 getter로 맵을 래핑하는 도우미 클래스를 고려하십시오.


1
실제로 Rich Hickey가 주장하는 바에 따르면, 데이터에 실제 행동이 중요하다면 "디자인"전체를 잘못하고있을 것입니다. 데이터는 "정보"입니다. 현실 세계의 정보는 "데이터가 저장되는 장소"가 아닙니다. 정보에는 "정보 변경 방법을 제어하는 ​​작업"이 없습니다. 우리는 사람들에게 정보가 저장된 위치를 알려 주어 정보를 전달하지 않습니다. 객체 지향적 은유는 때때로 세계의 적절한 모델이다. 그러나 종종 그렇지 않다. 그것이 "ypur 문제에 대해 생각하십시오"라고 그가 말한 것입니다. 모든 것이 대상은 아닙니다.
Virgil

0

의 API map에는 두 가지 수준이 있습니다.

  1. 지도 용 API입니다.
  2. 응용 프로그램의 규칙.

API는 규칙에 따라 맵에 설명 될 수 있습니다. 예를 들어, 쌍 :api api-validate을 맵에 배치하거나 :api-foo validate-foo컨벤션 일 수 있습니다. 지도도 저장할 수 있습니다 api api-documentation-link.

규칙을 사용하면 프로그래머는 맵으로 구현 된 "유형"에 대한 액세스를 표준화하는 도메인 별 언어를 만들 수 있습니다. 를 사용 (keys map)하면 런타임시 속성을 결정할 수 있습니다.

지도에 대해서는 마법이 없으며 물체에 대해서는 마법이 없습니다. 모두 파견입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.