JQuery có phải là một ví dụ về đối tượng của thần God hay không?


56

Tôi muốn hỏi - Tôi đang dần học jQuery.

Những gì tôi thấy là một ví dụ chính xác về mô hình chống đối tượng God . Về cơ bản, mọi thứ đều đi vào $chức năng, bất kể nó là gì.

Tôi có đúng không và jQuery có thực sự là một ví dụ về mô hình chống này không?


13
Có lẽ bạn đang hỏi sai câu hỏi. Câu hỏi đúng là: "Làm thế nào jQuery có thể được triển khai thỏa đáng trong ngôn ngữ Javascript theo cách không yêu cầu $chức năng hoặc jQueryđối tượng.
Robert Harvey

1
Tôi luôn muốn hỏi câu hỏi này thật sự :).
AnyOne

@RobertHarvey: thật đáng buồn, tôi không biết đủ về JavaScript để trả lời điều này.
Karel Bílek

Có (thêm 12 nhân vật nữa ...)
Andrea

Nó giống như một thư viện sửa chữa
Andrew

Câu trả lời:


54

Để trả lời câu hỏi đó, tôi sẽ hỏi bạn một câu hỏi tu từ về một cấu trúc khác có thuộc tính tương tự với các thành phần DOM mà jQuery thao tác, đó là trình lặp cũ tốt. Câu hỏi là:

Có bao nhiêu hoạt động nào bạn cần trên một iterator đơn giản?

Câu hỏi có thể được trả lời dễ dàng bằng cách xem xét bất kỳ API Iterator nào trong một ngôn ngữ nhất định. Bạn cần 3 phương pháp:

  1. Nhận giá trị hiện tại
  2. Di chuyển vòng lặp đến phần tử tiếp theo
  3. Kiểm tra xem Iterator có nhiều phần tử hơn không

Đó là tất cả những gì bạn cần. Nếu bạn có thể thực hiện 3 thao tác đó, bạn có thể thực hiện bất kỳ chuỗi phần tử nào.

Nhưng đó không chỉ là những gì bạn thường muốn làm với một chuỗi các yếu tố, phải không? Bạn thường có một mục tiêu cấp cao hơn nhiều để đạt được. Bạn có thể muốn làm một cái gì đó với mọi phần tử, bạn có thể muốn lọc chúng theo một số điều kiện hoặc một trong một số phương pháp khác. Xem giao diện IEnumerable trong thư viện LINQ trong .NET để biết thêm ví dụ.

Bạn có thấy có bao nhiêu không? Và đó chỉ là một tập hợp con của tất cả các phương thức mà họ có thể đã đưa vào giao diện IEnumerable, bởi vì bạn thường kết hợp chúng để đạt được các mục tiêu cao hơn.

Nhưng đây là xoắn. Những phương thức này không có trên giao diện IEnumerable. Chúng là các phương thức tiện ích đơn giản thực sự lấy IEnumerable làm đầu vào và làm một cái gì đó với nó. Vì vậy, trong ngôn ngữ C # có cảm giác như có một phương thức bajillion trên giao diện IEnumerable, IEnumerable không phải là một đối tượng thần.


Bây giờ trở lại jQuery. Hãy hỏi lại câu hỏi đó, lần này với phần tử DOM.

Bạn cần bao nhiêu thao tác trên một phần tử DOM?

Một lần nữa câu trả lời là khá đơn giản. Tất cả các phương thức bạn cần là các phương thức để đọc / sửa đổi các thuộc tính và các phần tử con. Đó là về nó. Mọi thứ khác chỉ là sự kết hợp của những hoạt động cơ bản đó.

Nhưng bạn muốn làm gì với các phần tử mức cao hơn bao nhiêu? Vâng, giống như một Iterator: một số lượng lớn những thứ khác nhau. Và đó là nơi jQuery xuất hiện. JQuery, về bản chất cung cấp hai điều:

  1. Một bộ sưu tập các phương thức tiện ích rất hay mà bạn có thể muốn gọi trên một phần tử DOM và;
  2. Đường cú pháp để sử dụng nó là một trải nghiệm tốt hơn nhiều so với sử dụng API DOM tiêu chuẩn.

Nếu bạn loại bỏ biểu mẫu có đường, bạn nhận ra rằng jQuery có thể dễ dàng được viết dưới dạng một loạt các hàm chọn / sửa đổi các phần tử DOM. Ví dụ:

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

... có thể được viết là:

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

Về mặt ngữ nghĩa, đó là điều chính xác. Tuy nhiên, biểu mẫu đầu tiên có lợi thế lớn là thứ tự từ trái sang phải của các câu lệnh tuân theo thứ tự các thao tác sẽ được thực thi. Bắt đầu thứ hai ở giữa, điều này làm cho mã rất khó đọc nếu bạn kết hợp nhiều thao tác với nhau.

Vì vậy, những gì sẽ làm tất cả có nghĩa gì? JQuery đó (như LINQ) không phải là mô hình chống đối tượng của Chúa. Thay vào đó là một trường hợp của một mẫu rất được tôn trọng gọi là Trang trí .


Nhưng một lần nữa, những gì về ghi đè $để làm tất cả những điều khác nhau? Vâng, đó chỉ là cú pháp đường thực sự. Tất cả các lệnh gọi $và các dẫn xuất của nó $.getJson()là những thứ hoàn toàn khác nhau chỉ xảy ra để chia sẻ các tên tương tự để bạn có thể cảm nhận ngay rằng chúng thuộc về jQuery. $thực hiện một và chỉ một nhiệm vụ: cho phép bạn có điểm bắt đầu dễ nhận biết để sử dụng jQuery. Và tất cả các phương thức mà bạn có thể gọi trên một đối tượng jQuery không phải là triệu chứng của một đối tượng thần. Chúng đơn giản là các hàm tiện ích khác nhau mà mỗi hàm thực hiện một và chỉ một thứ trên phần tử DOM được truyền dưới dạng đối số. Ký hiệu .dot chỉ có ở đây vì nó giúp viết mã dễ dàng hơn.


6
Một vật được trang trí có hàng trăm phương thức được thêm vào là một ví dụ tuyệt vời về Vật thể Thần. Việc nó trang trí một đối tượng được thiết kế tốt với một bộ phương thức hợp lý là bằng chứng bạn cần.
Ross Patterson

2
@RossPatterson Bạn có không đồng ý không? Nếu là bạn, tôi khuyến khích bạn đăng câu trả lời của riêng bạn. Tôi nghĩ rằng Laurent là tốt, nhưng tôi vẫn chưa quyết định.
Nicole

@RossPatterson Tôi giả sử nhận xét của bạn có nghĩa là đây là một trường hợp mà nó một Vật thể Thiên Chúa nhưng nó là một điều tốt. Tôi có nhầm không?
Laurent Bourgault-Roy

3
@ LaurentBourgault-Roy Tôi không đồng ý với kết luận của bạn - Tôi tin rằng jQuery thực sự là một ví dụ về God Object. Nhưng bạn đã làm một công việc tốt đến mức tôi không thể chịu đựng được câu trả lời của bạn. Cảm ơn cho một lời giải thích tuyệt vời về vị trí của bạn.
Ross Patterson

1
Tôi không đồng ý hoàn toàn với sự đồng tình. Có nhiều hàm tiện ích trên jQuery không liên quan đến thao tác DOM.
Boris Yankov

19

Không - $chức năng thực sự chỉ bị quá tải cho ba nhiệm vụ . Mọi thứ khác là các hàm con chỉ sử dụng nó làm không gian tên .


17
Nhưng nó trả về một đối tượng " jQuery " chứa nhiều API jQuery - và, tôi nghĩ, sẽ là đối tượng "Thần" mà OP đang đề cập.
Nicole

2
@NickC, mặc dù nó là một đối tượng, trong trường hợp đó tôi nghĩ rằng nó thực sự được sử dụng như một không gian tên, giống như Math. Vì không có không gian tên tích hợp trong JS, nên họ chỉ sử dụng một đối tượng cho điều đó. Mặc dù tôi không chắc chắn sự thay thế sẽ là gì? Đặt tất cả các chức năng và thuộc tính trong không gian tên toàn cầu?
nguyệt quế

5

Hàm jQuery chính (ví dụ $("div a")) về cơ bản là một phương thức xuất xưởng trả về một thể hiện của loại jQuery đại diện cho một tập hợp các phần tử DOM.

Các phiên bản của loại jQuery này có một số lượng lớn các phương thức thao tác DOM có sẵn, hoạt động trên các phần tử DOM được thể hiện bởi thể hiện. Mặc dù điều này có thể được coi là một lớp phát triển quá lớn, nhưng nó không thực sự phù hợp với mô hình God Object.

Cuối cùng, như Michael Borgwardt đề cập, cũng có một số lượng lớn các hàm tiện ích sử dụng $ làm không gian tên và chỉ liên quan một cách hữu hình với các đối tượng jQuery của bộ sưu tập DOM.


1
Tại sao nó không phù hợp với mẫu God Object?
Benjamin Hodgson

4

$ không phải là một đối tượng, nó là một không gian tên.

Bạn có thể gọi java.lang là một đối tượng thần vì có nhiều lớp không? Đây là cú pháp hoàn toàn hợp lệ để gọi java.lang.String.format(...), rất giống với cách gọi bất cứ thứ gì trên jQuery.

Một đối tượng, để trở thành một đối tượng thần, phải là một đối tượng thích hợp ngay từ đầu - chứa cả dữ liệu và trí thông minh để hành động theo dữ liệu. jQuery chỉ chứa các phương thức.

Một cách khác để xem xét nó: một thước đo tốt cho mức độ của một đối tượng thần là một sự gắn kết - sự gắn kết thấp hơn có nghĩa là nhiều hơn một đối tượng thần. Sự gắn kết nói rằng ow phần lớn dữ liệu được sử dụng bởi bao nhiêu phương thức. Vì không có dữ liệu trong jQuery, bạn thực hiện phép toán - tất cả các phương thức đều sử dụng tất cả dữ liệu, do đó jQuery rất gắn kết, do đó không phải là một đối tượng thần.


3

Benjamin yêu cầu tôi làm rõ vị trí của mình, vì vậy tôi đã chỉnh sửa bài viết trước của mình và thêm những suy nghĩ khác.

Bob Martin là tác giả của một cuốn sách tuyệt vời có tựa đề là Clean Code. Trong cuốn sách đó có một chương (Chương 6.) được gọi là các cấu trúc Đối tượng và Dữ liệu, ông thảo luận về sự khác biệt quan trọng nhất giữa các đối tượng và cấu trúc dữ liệu và tuyên bố rằng chúng ta phải chọn giữa chúng, bởi vì trộn chúng là một ý tưởng rất tồi.

Sự nhầm lẫn này đôi khi dẫn đến các cấu trúc lai không may là một nửa đối tượng và một nửa cấu trúc dữ liệu. Chúng có các hàm làm những việc quan trọng và chúng cũng có các biến công khai hoặc các bộ truy cập và bộ biến đổi công khai, với tất cả ý nghĩa và mục đích, làm cho các biến riêng tư công khai, cám dỗ các hàm bên ngoài khác sử dụng các biến đó theo cách mà một chương trình thủ tục sẽ sử dụng cấu trúc dữ liệu.4 Các giống lai như vậy làm cho việc thêm các chức năng mới trở nên khó khăn nhưng cũng làm cho việc thêm các cấu trúc dữ liệu mới trở nên khó khăn. Họ là tồi tệ nhất của cả hai thế giới. Tránh tạo ra chúng. Chúng là dấu hiệu cho thấy một thiết kế lầy lội mà các tác giả của chúng không chắc chắn về điều này hoặc tệ hơn, không biết gì về việc liệu họ có cần bảo vệ khỏi các chức năng hoặc các loại hay không.

Tôi nghĩ DOM là một ví dụ về các giống lai cấu trúc dữ liệu và đối tượng này. Ví dụ: bằng DOM, chúng tôi viết mã như thế này:

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 nên rõ ràng là một cấu trúc dữ liệu thay vì lai.

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();

Khung công tác jQuery là một loạt các thủ tục, có thể chọn và sửa đổi một tập hợp các nút DOM và thực hiện nhiều thứ khác. Như Laurent đã chỉ ra trong bài viết của mình, jQuery là một cái gì đó như thế này dưới mui xe:

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

Các nhà phát triển của jQuery đã hợp nhất tất cả các quy trình này thành một lớp duy nhất, chịu trách nhiệm cho tất cả các tính năng được liệt kê ở trên. Vì vậy, nó rõ ràng vi phạm Nguyên tắc Trách nhiệm duy nhất và vì vậy nó là một đối tượng của thần. Điều duy nhất bởi vì nó không phá vỡ bất cứ thứ gì, bởi vì nó là một lớp độc lập duy nhất hoạt động trên một cấu trúc dữ liệu duy nhất (tập hợp các nút DOM). Nếu chúng ta thêm các lớp con jQuery hoặc cấu trúc dữ liệu khác, dự án sẽ sụp đổ rất nhanh. Vì vậy, tôi không nghĩ rằng chúng ta có thể nói về oo bằng jQuery, nó khá thủ tục hơn oo mặc dù thực tế là nó định nghĩa một lớp.

Những gì Laurent tuyên bố là hoàn toàn vô nghĩa:

Vì vậy, những gì sẽ làm tất cả có nghĩa gì? JQuery đó (như LINQ) không phải là mô hình chống đối tượng của Chúa. Thay vào đó là một trường hợp của một mẫu rất được tôn trọng gọi là Trang trí.

Mẫu Decorator là về việc thêm chức năng mới bằng cách giữ giao diện và không sửa đổi các lớp hiện có. Ví dụ:

Bạn có thể định nghĩa 2 lớp thực hiện cùng một giao diện, nhưng với cách triển khai hoàn toàn khác nhau:

/**
 * @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
};

Nếu bạn có các phương thức chỉ sử dụng giao diện chung, thì bạn có thể xác định một hoặc nhiều Trình trang trí thay vì sao chép cùng một mã giữa A và B. Bạn có thể sử dụng các trình trang trí này ngay cả trong cấu trúc lồng nhau.

/**
 * @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);
};

Vì vậy, bạn có thể thay thế các thể hiện ban đầu bằng các thể hiện trang trí trong mã mức độ trừu tượng cao hơn.

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(...);

Kết luận rằng jQuery không phải là Công cụ trang trí của bất cứ thứ gì, vì nó không thực hiện cùng một giao diện như Array, NodeList hoặc bất kỳ đối tượng DOM nào khác. Nó thực hiện giao diện riêng của nó. Các mô-đun cũng không được sử dụng làm Trình trang trí, chúng chỉ đơn giản ghi đè lên nguyên mẫu ban đầu. Vì vậy, mẫu Decorator không được sử dụng trong toàn bộ lib lib. Lớp jQuery đơn giản là một bộ điều hợp khổng lồ cho phép chúng ta sử dụng cùng một API bởi nhiều trình duyệt khác nhau. Từ quan điểm oo, nó là một mớ hỗn độn, nhưng điều đó không thực sự quan trọng, nó hoạt động tốt và chúng tôi sử dụng nó.


Bạn dường như đang tranh luận rằng việc sử dụng các phương thức infix là chìa khóa khác nhau giữa mã OO và mã thủ tục. Tôi không nghĩ điều này là đúng. Bạn có thể làm rõ vị trí của bạn?
Benjamin Hodgson

@BenjaminHodgson Ý bạn là gì trong "phương pháp infix"?
inf3rno

@BenjaminHodgson Tôi đã làm rõ. Tôi hy vọng nó là rõ ràng bây giờ.
inf3rno
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.