jQuery는“신 객체”반 패턴의 예입니까?


56

묻고 싶습니다 – jQuery를 천천히 배우고 있습니다.

내가 보는 것은 God Object anti-pattern정확한 예 입니다 . 기본적으로 모든 것이 기능에 관계없이 기능에 적용됩니다.$

내가 옳고 jQuery가 실제로이 안티 패턴의 예입니까?


13
아마도 잘못된 질문을하고있을 것입니다. 올바른 질문은 " $함수 나 jQuery객체를 필요로하지 않는 방법으로 jQuery를 어떻게 자바 스크립트 언어로 만족스럽게 구현할 수 있을까?"
Robert Harvey

1
나는 항상이 질문을 정말로하고 싶었습니다 :).
AnyOne

@RobertHarvey : 슬프게도, 나는 이것에 대답 할 JavaScript에 대해 충분히 모른다.
Karel Bílek

예 (12 자 이상 추가 ...)
Andrea

그것은 교정 도서관과 같습니다
Andrew

답변:


54

이 질문에 대답하기 위해 jQuery가 조작하는 DOM 요소와 유사한 속성을 가진 또 다른 구조에 대한 수사적 질문을하겠습니다. 질문은 ~이야:

간단한 반복자에서 얼마나 많은 작업이 필요 합니까?

주어진 언어로 된 Iterator API를 보면 질문에 쉽게 대답 할 수 있습니다. 3 가지 방법이 필요합니다.

  1. 현재 가치를 얻으십시오
  2. 반복자를 다음 요소로 이동
  3. 반복자에 더 많은 요소가 있는지 확인하십시오.

그게 당신이 필요한 전부입니다. 이 3 가지 작업을 수행 할 수 있으면 모든 요소 시퀀스를 통과 할 수 있습니다.

그러나 그것은 당신이 일반적으로 일련의 요소로하고 싶은 것이 아닙니다. 일반적으로 달성해야 할 수준이 훨씬 높습니다. 모든 요소로 무언가를하고 싶을 수도 있고, 조건이나 다른 여러 방법 중 하나에 따라 필터링 할 수도 있습니다. 자세한 예는 .NET의 LINQ 라이브러리에 있는 IEnumerable 인터페이스 를 참조하십시오 .

당신은 얼마나 많은 것을 봅니까? 이는 IEnumerable 인터페이스에 적용 할 수있는 모든 방법의 일부일뿐입니다. 일반적으로 더 높은 목표를 달성하기 위해 이들을 결합하기 때문입니다.

그러나 여기 트위스트가 있습니다. 이러한 메소드는 IEnumerable 인터페이스에 없습니다. 그것들은 실제로 IEnumerable을 입력으로 취하고 그것으로 무언가를하는 간단한 유틸리티 메소드입니다. 따라서 C # 언어에서는 IEnumerable 인터페이스에 bajillion 메소드가있는 것처럼 느껴지지만 IEnumerable은 신 개체가 아닙니다.


이제 jQuery로 돌아갑니다. 이번에는 DOM 요소를 사용하여이 질문을 다시하겠습니다.

DOM 요소에 얼마나 많은 작업이 필요합니까?

다시 한 번 대답은 매우 간단합니다. 필요한 모든 메소드는 속성 및 하위 요소를 읽고 수정하는 메소드입니다. 그게 다야. 다른 모든 것들은 그 기본 작업의 조합 일뿐입니다.

그러나 DOM 요소로 얼마나 높은 수준의 작업을 원하십니까? 글쎄, Iterator와 같습니다 : bajillion의 다른 것들. 그리고 여기서 jQuery가 시작됩니다. jQuery는 본질적으로 두 가지를 제공합니다.

  1. DOM 요소를 호출 할 수있는 매우 유용한 유틸리티 메소드 모음.
  2. 그것을 사용하는 것이 표준 DOM API를 사용하는 것보다 훨씬 나은 경험을 할 수 있도록 구문 설탕.

설탕 형태를 취하면 jQuery가 DOM 요소를 선택 / 수정하는 여러 함수로 쉽게 작성 될 수 있다는 것을 알게됩니다. 예를 들면 다음과 같습니다.

$("#body").html("<p>hello</p>");

... 다음과 같이 작성되었을 수 있습니다.

html($("#body"), "<p>hello</p>");

의미 상 정확히 똑같습니다. 그러나 첫 번째 양식은 명령문의 왼쪽에서 오른쪽 순서가 조작이 실행될 순서를 따른다는 큰 장점이 있습니다. 중간에 두 번째 시작으로 많은 작업을 결합하면 코드를 읽기가 매우 어렵습니다.

그래서 그것은 무엇을 의미합니까? LINQ와 같은 jQuery는 God 객체 안티 패턴이 아닙니다. 대신 Decorator 라는 매우 존경받는 패턴의 경우입니다 .


그러나 다시, $다른 모든 일을하는 것에 대한 재정의는 어떻습니까? 글쎄, 그것은 단지 구문 설탕입니다. 에 대한 모든 호출 $과 그 파생어 $.getJson()는 비슷한 이름을 공유하는 완전히 다른 일이므로 jQuery에 속한다고 즉시 느낄 수 있습니다 . $하나의 작업 만 수행합니다. jQuery를 사용하기 쉽게 식별 할 수있는 시작점이 있습니다. 그리고 jQuery 객체에서 호출 할 수있는 모든 메소드는 god 객체의 증상이 아닙니다. 그것들은 인수로 전달 된 DOM 요소에서 각각 하나만 수행하는 다른 유틸리티 함수입니다. .dot 표기법은 코드 작성을 더 쉽게하기 때문에 여기에만 있습니다.


6
수백 가지의 추가 된 메소드가있는 데코레이션 된 오브젝트 는 신 오브젝트 의 좋은 예입니다. 합리적인 디자인 방법으로 잘 설계된 객체를 장식한다는 사실은 필요한 증거입니다.
로스 패터슨

2
@RossPatterson 당신이 동의하지 않습니까? 당신이 있다면, 나는 당신이 당신의 자신의 답변을 게시하는 것이 좋습니다. 나는 Laurent의 것이 좋다고 생각하지만 여전히 미정입니다.
Nicole

@RossPatterson 나는 당신의 의견은 의미 가정이는 경우가 있다 하나님 개체 하지만 그것은 좋은 일이다. 내가 착각 했니?
Laurent Bourgault-Roy

3
@ LaurentBourgault-Roy 나는 당신의 결론에 동의하지 않습니다-jQuery는 실제로 God Object 예제라고 생각합니다. 그러나 당신은 당신의 대답을 공감하기 위해 견딜 수없는 훌륭한 일을했습니다. 당신의 입장에 대한 훌륭한 설명에 감사드립니다.
로스 패터슨

1
나는 conculsion에 완전히 동의하지 않는다. jQuery에는 DOM 조작과 관련이없는 많은 유틸리티 함수가 있습니다.
Boris Yankov

19

아니요-이 $기능은 실제로 세 가지 작업에 대해서만 오버로드됩니다 . 그 밖의 모든 것은 네임 스페이스 로 사용하는 자식 함수입니다 .


17
그러나 많은 jQuery API를 포함 하는 " jQuery " 객체 를 반환 합니다. OP가 참조하는 "God"객체라고 생각합니다.
Nicole

2
@NickC는 객체이지만이 경우 실제로는 네임 스페이스로 사용되는 것처럼 생각합니다 Math. JS에는 내장 네임 스페이스가 없으므로 객체를 사용합니다. 대안이 무엇인지 잘 모르겠지만? 모든 기능과 속성을 전역 네임 스페이스에 넣습니까?
laurent

5

기본 jQuery 함수 (예 :) $("div a")는 본질적 으로 DOM 요소의 컬렉션을 나타내는 jQuery 유형 의 인스턴스 를 반환하는 팩토리 메소드입니다 .

이러한 경우 JQuery와 유형의 인스턴스에 의해 표현되는 DOM 요소에 작동 가능 DOM 조작 방법의 수가 많다. 이 클래스는 너무 큰 클래스로 간주 될 수 있지만 실제로는 God Object 패턴에 맞지 않습니다.

마지막으로 Michael Borgwardt가 언급했듯이 $를 네임 스페이스로 사용하고 DOM 컬렉션 jQuery 객체와 접하는 관련 유틸리티 함수도 많이 있습니다.


1
왜 God Object 패턴에 맞지 않습니까?
Benjamin Hodgson

4

$ 개체가 아니라 네임 스페이스입니다.

java.lang을 포함하는 많은 클래스 때문에 java.lang을 god 객체라고 부릅니까? java.lang.String.format(...)호출하는 것은 절대적으로 유효한 구문 이며, jQuery에서 무엇이든 호출하는 것과 매우 유사합니다.

신의 대상이 되려면 대상은 데이터와 데이터에 작용하는 지능이 모두 들어있는 적절한 대상이어야합니다. jQuery는 메소드 만 포함합니다.

그것을 보는 또 다른 방법 : 물체가 얼마나 많은 신 물체인지에 대한 좋은 척도는 응집력입니다. 응집력이 낮을수록 신의 물체가 더 많습니다. 응집력은 많은 데이터가 얼마나 많은 방법으로 사용되는지를 말한다. jQuery에는 데이터가 없기 때문에 수학을 수행합니다. 모든 메소드는 모든 데이터를 사용하므로 jQuery는 응집력이 뛰어나므로 신의 대상이 아닙니다.


3

Benjamin은 저의 입장을 분명히 해달라고 요청했습니다. 그래서 이전 게시물을 편집하고 더 많은 생각을 추가했습니다.

Bob Martin은 Clean Code라는 제목의 훌륭한 책의 저자입니다. 이 책에는 객체와 데이터 구조라고하는 장 (6 장)이 있는데, 여기에는 객체와 데이터 구조의 가장 중요한 차이점과 이들 사이에서 선택해야하는 클레임에 대해 논의하는 것이 매우 좋지 않습니다.

이 혼동은 때때로 반 객체와 반 데이터 구조 인 불행한 하이브리드 구조로 이어집니다. 중요한 기능을 수행하는 함수가 있으며, 모든 의도와 목적을 위해 비공개 변수를 공개하고 다른 외부 함수가 해당 변수를 절차 프로그램에서 사용하는 방식으로 사용하도록 유혹하는 공개 변수 또는 공개 접근 자 및 변경자가 있습니다 그러한 하이브리드는 새로운 기능을 추가하는 것을 어렵게하지만 새로운 데이터 구조를 추가하는 것을 어렵게 만든다. 그들은 두 세계에서 최악입니다. 작성하지 마십시오. 그것들은 저자가 기능이나 유형으로부터 보호가 필요한지 확신 할 수없는 (혹은 더 나쁘거나) 불분명 한 디자인을 나타냅니다.

DOM은 이러한 객체 및 데이터 구조 하이브리드의 예라고 생각합니다. 예를 들어 DOM은 다음과 같은 코드를 작성합니다.

el.appendChild(node);
el.childNodes;
// bleeding internals

el.setAttribute(attr, val);
el.attributes;
// bleeding internals

el.style.color;
// at least this is okay

el = document.createElement(tag);
doc = document.implementation.createHTMLDocument();
// document is both a factory and a tree root

DOM은 분명히 하이브리드 대신 데이터 구조 여야합니다.

el.childNodes.add(node);
// or el.childNodes[el.childNodes.length] = node;
el.childNodes;

el.attributes.put(attr, val);
// or el.attributes[attr] = val;
el.attributes;

el.style.get("color"); 
// or el.style.color;

factory = new HtmlNodeFactory();
el = factory.createElement(document, tag);
doc = factory.createDocument();

jQuery 프레임 워크는 DOM 노드 모음을 선택 및 수정하고 다른 많은 작업을 수행 할 수있는 일련의 프로 시저입니다. Laurent가 자신의 게시물에서 지적했듯이 jQuery는 다음과 같습니다.

html(select("#body"), "<p>hello</p>");

jQuery 개발자는 이러한 모든 프로 시저를 단일 클래스로 병합하여 위에 나열된 모든 기능을 담당합니다. 따라서 그것은 단일 책임 원칙을 명백히 위반 하므로 신의 대상입니다. 하나의 데이터 구조 (DOM 노드 모음)에서 작동하는 단일 독립형 클래스이기 때문에 아무것도 깨뜨리지 않기 때문에 유일한 것입니다. jQuery 서브 클래스 또는 다른 데이터 구조를 추가하면 프로젝트가 매우 빠르게 붕괴됩니다. 따라서 jQuery로 oo에 대해 이야기 할 수 있다고 생각하지 않습니다. 클래스를 정의한다는 사실에도 불구하고 oo보다 절차 적입니다.

Laurent가 주장하는 것은 완벽한 말도 안됩니다.

그래서 그것은 무엇을 의미합니까? LINQ와 같은 jQuery는 God 객체 안티 패턴이 아닙니다. 대신 Decorator라는 매우 존경받는 패턴의 경우입니다.

데코레이터 패턴은 인터페이스를 유지하고 기존 클래스를 수정하지 않음으로써 새로운 기능을 추가하는 것입니다. 예를 들면 다음과 같습니다.

동일한 인터페이스를 구현하지만 완전히 다른 구현으로 2 개의 클래스를 정의 할 수 있습니다.

/**
 * @interface
 */
var Something = function (){};
/**
 * @argument {string} arg1 The first argument.
 * @argument {string} arg2 The second argument.
 */
Something.prototype.doSomething = function (arg1, arg2){};

/**
 * @class
 * @implements {Something}
 */
var A = function (){
    // ...
};
/**
 * @argument {string} arg1 The first argument.
 * @argument {string} arg2 The second argument.
 */
A.prototype.doSomething = function (arg1, arg2){
    // doSomething implementation of A
};

/**
 * @class
 * @implements {Something}
 */
var B = function (){
    // ...
};
/**
 * @argument {string} arg1 The first argument.
 * @argument {string} arg2 The second argument.
 */
B.prototype.doSomething = function (arg1, arg2){
    // doSomething implementation of B
    // it is completely different from the implementation of A
    // that's why it cannot be a sub-class of A
};

공통 인터페이스 만 사용하는 메소드가있는 경우 A와 B간에 동일한 코드를 복사하여 붙여 넣는 대신 하나 이상의 데코레이터를 정의 할 수 있습니다. 중첩 된 구조에서도 이러한 데코레이터를 사용할 수 있습니다.

/**
 * @class
 * @implements {Something}
 * @argument {Something} something The decorated object.
 */
var SomethingDecorator = function (something){
    this.something = something;
    // ...
};

/**
 * @argument {string} arg1 The first argument.
 * @argument {string} arg2 The second argument.
 */
SomethingDecorator.prototype.doSomething = function (arg1, arg2){
    return this.something.doSomething(arg1, arg2);
};

/**
 * A new method which can be common by A and B. 
 * 
 * @argument {function} done The callback.
 * @argument {string} arg1 The first argument.
 * @argument {string} arg2 The second argument.
 */
SomethingDecorator.prototype.doSomethingDelayed = function (done, arg1, arg2){
    var err, res;
    setTimeout(function (){
        try {
            res = this.doSomething(o.arg1, o.arg2);
        } catch (e) {
            err = e;
        }
        callback(err, res);
    }, 1000);
};

따라서 더 높은 추상화 레벨 코드에서 원래 인스턴스를 데코레이터 인스턴스로 대체 할 수 있습니다.

function decorateWithManyFeatures(something){
    var d1 = new SomethingDecorator(something);
    var d2 = new AnotherSomethingDecorator(d1);
    // ...
    return dn;
}

var a = new A();
var b = new B();
var decoratedA = decorateWithManyFeatures(a);
var decoratedB = decorateWithManyFeatures(b);

decoratedA.doSomethingDelayed(...);
decoratedB.doSomethingDelayed(...);

jQuery는 Array, NodeList 또는 다른 DOM 객체와 동일한 인터페이스를 구현하지 않기 때문에 어떤 데코레이터도 아니라는 결론을 내립니다. 자체 인터페이스를 구현합니다. 모듈은 데코레이터로 사용되지 않으며 단순히 원본 프로토 타입을 무시합니다. 따라서 Decorator 패턴은 전체 jQuery 라이브러리에서 사용되지 않습니다. jQuery 클래스는 다양한 브라우저에서 동일한 API를 사용할 수있게하는 거대한 어댑터입니다. oo 관점에서 볼 때 그것은 완전한 혼란이지만 실제로 중요하지 않으며 잘 작동하며 우리는 그것을 사용합니다.


당신은 OO 코드와 절차 적 코드 사이에서 중요한 방법으로 infix 메소드를 사용한다고 주장하는 것 같습니다. 나는 이것이 사실이라고 생각하지 않습니다. 당신의 입장을 명확히 할 수 있습니까?
Benjamin Hodgson

@BenjaminHodgson "infix method"의 의미는 무엇입니까?
inf3rno

@ BenjaminHodgson 나는 명확히했다. 나는 그것이 분명하기를 바랍니다.
inf3rno
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.