jQuery เป็นตัวอย่างของ antipattern“ พระเจ้าวัตถุ” หรือไม่


56

ฉันต้องการถาม - ฉันเรียนรู้ช้า jQuery

สิ่งที่ฉันเห็นเป็นตัวอย่างที่แน่นอนของพระเจ้าวัตถุต่อต้านรูปแบบ โดยพื้นฐานแล้วทุกอย่างจะเข้าสู่$ฟังก์ชั่นไม่ว่าจะเป็นอะไรก็ตาม

ฉันพูดถูกและเป็น jQuery จริง ๆ แล้วเป็นตัวอย่างของรูปแบบการต่อต้านนี้หรือไม่


13
คุณอาจถามคำถามผิด คำถามที่ถูกต้องคือ "jQuery สามารถนำไปใช้อย่างน่าพอใจในภาษา Javascript อย่างไรในแบบที่ไม่ต้องการ$ฟังก์ชันหรือjQueryวัตถุ
Robert Harvey

1
ฉันอยากจะถามคำถามนี้เสมอ :)
AnyOne

@RobertHarvey: น่าเสียดายฉันไม่รู้ JavaScript เกี่ยวกับการตอบคำถามนี้
Karel Bílek

ใช่ (เพิ่มอีก 12 ตัวอักษรเพื่อไป ... )
Andrea

มันเหมือนห้องสมุดที่ถูกต้องมากขึ้น
Andrew

คำตอบ:


54

เพื่อตอบคำถามนั้นฉันจะถามคำถามเชิงโวหารเกี่ยวกับโครงสร้างอื่นที่มีคุณสมบัติคล้ายกับองค์ประกอบ DOM ที่ jQuery ใช้งานนั่นคือตัววนรอบตัวเก่าที่ดี คำถามคือ:

คุณต้องใช้การทำงานกี่อย่างในตัววนซ้ำแบบง่าย?

คำถามสามารถตอบได้อย่างง่ายดายโดยดูที่ Iterator API ในภาษาที่กำหนด คุณต้องการ 3 วิธี:

  1. รับค่าปัจจุบัน
  2. ย้ายตัววนซ้ำไปยังองค์ประกอบถัดไป
  3. ตรวจสอบว่า Iterator มีองค์ประกอบมากขึ้น

นั่นคือทั้งหมดที่คุณต้องการ หากคุณสามารถดำเนินการ 3 อย่างเหล่านั้นคุณสามารถไปตามลำดับขององค์ประกอบใด ๆ

แต่นั่นไม่ใช่แค่สิ่งที่คุณมักต้องการทำกับลำดับขององค์ประกอบเท่านั้นใช่ไหม คุณมักจะมีเป้าหมายในระดับที่สูงขึ้นเพื่อให้บรรลุ คุณอาจต้องการทำบางสิ่งบางอย่างกับทุกองค์ประกอบคุณอาจต้องการกรองตามเงื่อนไขบางอย่างหรือหนึ่งในวิธีอื่น ๆ ดูอินเทอร์เฟซ IEnumerableในไลบรารี LINQ ใน. NET สำหรับตัวอย่างเพิ่มเติม

คุณเห็นว่ามีกี่คน? และนั่นเป็นเพียงส่วนย่อยของวิธีการทั้งหมดที่พวกเขาสามารถใส่ลงในอินเทอร์เฟซ IEnumerable เพราะคุณมักจะรวมพวกเขาเพื่อให้บรรลุเป้าหมายที่สูงขึ้น

แต่นี่คือการบิด วิธีการเหล่านั้นไม่ได้อยู่ในส่วนต่อประสาน IEnumerable พวกเขาเป็นวิธีการยูทิลิตี้อย่างง่ายที่จริง IEnumerable เป็นอินพุตและทำอะไรกับมัน ดังนั้นในภาษา C # ให้ความรู้สึกเหมือนมีวิธีการมากมายมหาศาลบนอินเทอร์เฟซ IEnumerable IEnumerable ไม่ใช่วัตถุเทพเจ้า


ตอนนี้กลับไปที่ jQuery ให้ถามคำถามนั้นอีกครั้งคราวนี้มีองค์ประกอบ DOM

คุณต้องการการดำเนินการจำนวนเท่าใดในองค์ประกอบ DOM

อีกครั้งคำตอบคือตรงไปตรงมาสวย วิธีการทั้งหมดที่คุณต้องการคือวิธีการอ่าน / แก้ไขคุณสมบัติและองค์ประกอบลูก เกี่ยวกับมัน. ทุกสิ่งอย่างอื่นเป็นเพียงการรวมกันของการดำเนินงานขั้นพื้นฐานเหล่านั้น

แต่คุณต้องการจะทำอะไรกับองค์ประกอบระดับสูงกว่านี้มากเท่าใด เช่นเดียวกับ Iterator: สิ่งที่แตกต่างกันมากถึงพันล้าน และนั่นคือสิ่งที่ jQuery เข้ามา jQuery ในสาระสำคัญมีสองสิ่ง:

  1. คอลเลกชันที่ดีมากของวิธีการสาธารณูปโภคที่คุณอาจต้องการโทรหาองค์ประกอบ DOM และ;
  2. น้ำตาลซินแทกติกเพื่อให้การใช้มันเป็นประสบการณ์ที่ดีกว่าการใช้ DOM API มาตรฐาน

หากคุณนำฟอร์ม sugared ออกมาคุณจะรู้ว่า jQuery สามารถเขียนได้ง่าย ๆ เป็นฟังก์ชั่นมากมายที่เลือก / แก้ไของค์ประกอบ DOM ตัวอย่างเช่น:

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

... สามารถเขียนเป็น:

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

ความหมายมันเป็นสิ่งเดียวกัน อย่างไรก็ตามรูปแบบแรกมีข้อได้เปรียบที่ยิ่งใหญ่ซึ่งคำสั่งจากซ้ายไปขวาของคำสั่งจะเป็นไปตามลำดับที่การดำเนินการจะถูกดำเนินการ การเริ่มต้นครั้งที่สองที่อยู่ตรงกลางซึ่งทำให้การอ่านโค้ดยากมากถ้าคุณรวมการทำงานหลายอย่างเข้าด้วยกัน

ดังนั้นมันหมายความว่าอย่างไร jQuery นั้น (เช่น LINQ) ไม่ใช่รูปแบบต่อต้านพระเจ้า มันแทนกรณีของรูปแบบที่เคารพนับถือมากเรียกว่าเป็นมัณฑนากร


แต่แล้วอีกครั้งสิ่งที่เกี่ยวกับการแทนที่ของ$การทำสิ่งต่าง ๆ เหล่านั้นทั้งหมดหรือไม่ นั่นคือน้ำตาลซินแทคติคจริงๆ การเรียกร้อง$และสัญญาซื้อขายล่วงหน้า$.getJson()ทั้งหมดเป็นสิ่งที่แตกต่างอย่างสิ้นเชิงที่เพิ่งเกิดขึ้นเพื่อแบ่งปันชื่อที่คล้ายกันเพื่อให้คุณสามารถรู้สึกได้ทันทีว่าพวกเขาเป็นของ jQuery $ดำเนินการเพียงงานเดียวเท่านั้น: ให้คุณมีจุดเริ่มต้นที่จำได้ง่ายในการใช้ jQuery และวิธีการทั้งหมดที่คุณสามารถเรียกใช้บนวัตถุ jQuery ไม่ใช่อาการของวัตถุพระเจ้า พวกเขาเป็นฟังก์ชั่นยูทิลิตี้ที่แตกต่างกันเพียงอย่างเดียวที่แต่ละคนทำสิ่งหนึ่งและสิ่งเดียวในองค์ประกอบ DOM ส่งผ่านเป็นอาร์กิวเมนต์ สัญลักษณ์. dot อยู่ที่นี่เพียงเพราะทำให้การเขียนโค้ดง่ายขึ้น


6
วัตถุตกแต่งที่มีวิธีการเพิ่มหลายร้อยวิธีเป็นตัวอย่างที่ดีของวัตถุพระเจ้า ความจริงที่ว่ามันตกแต่งวัตถุที่ออกแบบมาอย่างดีพร้อมชุดวิธีการที่เหมาะสมคือหลักฐานที่คุณต้องการ
Ross Patterson

2
@RossPatterson คุณไม่เห็นด้วยหรือไม่ ถ้าคุณเป็นฉันจะแนะนำให้คุณโพสต์คำตอบของคุณเอง ฉันคิดว่าของ Laurent นั้นดี แต่ฉันก็ยังไม่แน่ใจ
นิโคล

@ RossPatterson ฉันถือว่าความคิดเห็นของคุณหมายความว่านี่เป็นกรณีที่มันเป็นวัตถุของพระเจ้าแต่มันเป็นสิ่งที่ดี ฉันเข้าใจผิด
Laurent Bourgault-Roy

3
@ LaurentBourgault-Roy ฉันไม่เห็นด้วยกับข้อสรุปของคุณ - ฉันเชื่อว่า jQuery เป็นตัวอย่างของ Object Object แต่คุณทำได้ดีมากจนฉันทนไม่ได้ที่จะตอบคำถามของคุณ ขอบคุณสำหรับคำอธิบายที่ดีเกี่ยวกับตำแหน่งของคุณ
Ross Patterson

1
ฉันไม่เห็นด้วยกับการกดขี่ มีฟังก์ชั่นยูทิลิตี้มากมายใน jQuery ที่ไม่เกี่ยวข้องกับการจัดการ DOM
Boris Yankov

19

ไม่ - $ฟังก์ชั่นนี้ทำงานหนักเกินไปจริง ๆสำหรับสามงานเท่านั้น ทุกสิ่งทุกอย่างเป็นหน้าที่ของเด็กที่เพิ่งใช้เป็นnamespace


17
แต่มันจะคืนค่าวัตถุ " jQuery " ซึ่งมี jQuery API เป็นจำนวนมาก - และฉันคิดว่าน่าจะเป็นวัตถุ "พระเจ้า" ที่ OP อ้างถึง
นิโคล

2
@NickC แม้ว่ามันจะเป็นวัตถุ แต่ในกรณีนี้ฉันคิดว่ามันใช้เป็นเนมสเปซอย่างMathแน่นอน เนื่องจากไม่มี namespace ในตัวใน JS พวกเขาเพียงใช้วัตถุสำหรับสิ่งนั้น แม้ว่าฉันไม่แน่ใจว่าทางเลือกอื่นจะเป็นอย่างไร ใส่ฟังก์ชั่นและคุณสมบัติทั้งหมดในเนมสเปซส่วนกลางหรือไม่
Laurent

5

ฟังก์ชั่น jQuery หลัก (เช่น$("div a")) เป็นหลักวิธีการโรงงานที่ส่งกลับตัวอย่างของประเภท jQuery ที่แสดงถึงคอลเลกชันขององค์ประกอบ DOM

อินสแตนซ์ของชนิด jQuery เหล่านี้มีวิธีการจัดการ DOM จำนวนมากซึ่งมีให้ใช้งานบนองค์ประกอบ DOM ที่แสดงโดยอินสแตนซ์ แม้ว่าสิ่งนี้จะถือว่าเป็นคลาสที่โตใหญ่เกินไป แต่ก็ไม่สอดคล้องกับรูปแบบของ Object Object

ในที่สุดตามที่ Michael Borgwardt กล่าวถึงก็มีฟังก์ชั่นยูทิลิตี้จำนวนมากที่ใช้ $ เป็น namespace และมีความเกี่ยวข้องกับวัตถุ jQuery ของคอลเลกชัน DOM เท่านั้น


1
ทำไมมันไม่เหมาะกับรูปแบบวัตถุของพระเจ้า?
Benjamin Hodgson

4

$ ไม่ใช่วัตถุมันเป็นเนมสเปซ

คุณจะเรียก java.lang อ็อบเจกต์เทพเจ้าเพราะมีหลายคลาสในนั้นหรือไม่? มันเป็นไวยากรณ์ที่ถูกต้องอย่างแน่นอนในการโทรjava.lang.String.format(...)คล้ายกันมากในรูปแบบการโทรอะไรบน jQuery

วัตถุเพื่อที่จะเป็นวัตถุพระเจ้าจะต้องเป็นวัตถุที่เหมาะสมในสถานที่แรก - มีทั้งข้อมูลและความฉลาดในการดำเนินการกับข้อมูล jQuery มีวิธีการเท่านั้น

อีกวิธีหนึ่งในการมองดู: การวัดที่ดีสำหรับวัตถุเทพเจ้ามากว่าวัตถุคือการติดต่อกัน - การทำงานร่วมกันที่ต่ำกว่าหมายถึงวัตถุของพระเจ้ามากขึ้น การทำงานร่วมกันบอกว่าเป็นส่วนหนึ่งของข้อมูลที่ถูกใช้โดยวิธีการหลายวิธี เนื่องจากไม่มีข้อมูลใน jQuery คุณทำคณิตศาสตร์ - ทุกวิธีใช้ข้อมูลทั้งหมดดังนั้น jQuery จึงมีความเหนียวแน่นสูงดังนั้นจึงไม่ใช่วัตถุพระเจ้า


3

เบนจามินขอให้ฉันชี้แจงตำแหน่งของฉันดังนั้นฉันจึงแก้ไขโพสต์ก่อนหน้าของฉันและเพิ่มความคิดเพิ่มเติม

บ็อบมาร์ตินเป็นผู้แต่งหนังสือยอดเยี่ยมเรื่อง Clean Code ในหนังสือเล่มนั้นมีบท (บทที่ 6) ที่เรียกว่าวัตถุและโครงสร้างข้อมูลซึ่งเขากล่าวถึงความแตกต่างที่สำคัญที่สุดระหว่างวัตถุและโครงสร้างข้อมูลและอ้างว่าเราต้องเลือกระหว่างพวกเขาเพราะการผสมพวกเขาเป็นความคิดที่ไม่ดีมาก

ความสับสนนี้บางครั้งนำไปสู่โครงสร้างไฮบริดที่โชคร้ายที่เป็นวัตถุครึ่งหนึ่งและโครงสร้างข้อมูลครึ่งหนึ่ง พวกเขามีฟังก์ชั่นที่ทำสิ่งที่สำคัญและพวกเขายังมีตัวแปรสาธารณะหรือสาธารณะ accessors และ mutators ที่สำหรับทุก intents และวัตถุประสงค์ทำให้ตัวแปรส่วนตัวสาธารณะสาธารณะดึงดูดฟังก์ชั่นภายนอกอื่น ๆ ที่จะใช้ตัวแปรเหล่านั้นวิธีที่โปรแกรมขั้นตอนจะใช้ โครงสร้างข้อมูล 4 ลูกผสมดังกล่าวทำให้ยากที่จะเพิ่มฟังก์ชั่นใหม่ แต่ก็ทำให้ยากที่จะเพิ่มโครงสร้างข้อมูลใหม่ พวกเขาเลวร้ายที่สุดของทั้งสองโลก หลีกเลี่ยงการสร้างพวกเขา พวกเขาบ่งบอกถึงการออกแบบที่ยุ่งเหยิงซึ่งผู้เขียนไม่แน่ใจ - หรือแย่กว่าไม่รู้ว่าพวกเขาต้องการการปกป้องจากฟังก์ชั่นหรือประเภท

ฉันคิดว่า 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 และทำสิ่งอื่น ๆ อีกมากมาย ลอเรนต์ชี้ให้เห็นในโพสต์ของเขา jQuery เป็นอะไรเช่นนี้ภายใต้ประทุน:

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

ผู้พัฒนา jQuery ได้รวมขั้นตอนเหล่านี้ทั้งหมดไว้ในชั้นเรียนเดียวซึ่งมีหน้าที่รับผิดชอบต่อคุณสมบัติทั้งหมดที่ระบุไว้ข้างต้น ดังนั้นจึงเป็นการละเมิดหลักการความรับผิดชอบเดี่ยวอย่างชัดเจนและดังนั้นจึงเป็นวัตถุเทพเจ้า สิ่งเดียวเพราะมันไม่ทำลายอะไรเพราะมันเป็นคลาสแบบสแตนด์อโลนเดียวซึ่งทำงานในโครงสร้างข้อมูลเดียว (คอลเลกชันของโหนด DOM) ถ้าเราจะเพิ่มคลาสย่อย jQuery หรือโครงสร้างข้อมูลอื่นโครงการจะยุบอย่างรวดเร็ว ดังนั้นฉันไม่คิดว่าเราสามารถพูดคุยเกี่ยวกับ oo โดย jQuery มันค่อนข้างเป็นขั้นตอนกว่า oo แม้ว่าจะมีการกำหนดชั้นเรียน

สิ่งที่ Laurent อ้างว่าเป็นเรื่องไร้สาระสมบูรณ์:

ดังนั้นมันหมายความว่าอย่างไร jQuery นั้น (เช่น LINQ) ไม่ใช่รูปแบบต่อต้านพระเจ้า มันเป็นกรณีของรูปแบบที่น่านับถืออย่างมากที่เรียกว่ามัณฑนากร

รูปแบบของ 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
};

หากคุณมีวิธีการที่ใช้อินเทอร์เฟซทั่วไปเท่านั้นคุณสามารถกำหนด Decorator หนึ่งรายการขึ้นไปแทนที่จะคัดลอกโค้ดที่เหมือนกันระหว่าง 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 ไม่ใช่ Decorator ของสิ่งใดเพราะมันไม่ได้ใช้อินเทอร์เฟซเดียวกันกับ Array, NodeList หรือวัตถุ DOM อื่นใด มันใช้อินเทอร์เฟซของตัวเอง ไม่ได้ใช้โมดูลเป็นตัวตกแต่งเช่นกันพวกมันก็แค่แทนที่ต้นแบบดั้งเดิม ดังนั้นรูปแบบมัณฑนากรจึงไม่ได้ใช้ใน jQuery lib ทั้งหมด คลาส jQuery เป็นเพียงอะแดปเตอร์ขนาดใหญ่ซึ่งให้เราใช้ API เดียวกันโดยเบราว์เซอร์ที่แตกต่างกันมากมาย จากมุมมองของ oo มันเป็นระเบียบที่สมบูรณ์ แต่นั่นไม่สำคัญเลยมันทำงานได้ดีและเราใช้มัน


คุณดูเหมือนจะเถียงว่าการใช้วิธีการมัดเป็นกุญแจสำคัญที่แตกต่างระหว่างรหัส OO และรหัสขั้นตอน ฉันไม่คิดว่านี่เป็นเรื่องจริง คุณช่วยอธิบายตำแหน่งของคุณได้ไหม?
Benjamin Hodgson

@BenjaminHodgson คุณหมายถึงอะไรภายใต้ "วิธีการเติม"?
inf3rno

@BenjaminHodgson ฉันชี้แจง ฉันหวังว่ามันชัดเจนตอนนี้
inf3rno
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.