เหตุใดจึงใช้รูปแบบการเผยแพร่ / สมัครสมาชิก (ใน JS / jQuery)


103

ดังนั้นเพื่อนร่วมงานแนะนำฉันให้รู้จักกับรูปแบบการเผยแพร่ / สมัครสมาชิก (ใน JS / jQuery) แต่ฉันมีปัญหาในการจับใจความว่าทำไมจึงใช้รูปแบบนี้กับ JavaScript / jQuery 'ปกติ'

ตัวอย่างเช่นก่อนหน้านี้ฉันมีรหัสต่อไปนี้ ...

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    var orders = $(this).parents('form:first').find('div.order');
    if (orders.length > 2) {
        orders.last().remove();
    }
});

และฉันจะได้เห็นบุญของการทำสิ่งนี้แทนเช่น ...

removeOrder = function(orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    removeOrder($(this).parents('form:first').find('div.order'));
});

เนื่องจากแนะนำความสามารถในการใช้removeOrderฟังก์ชันซ้ำสำหรับเหตุการณ์ต่างๆเป็นต้น

แต่ทำไมคุณถึงตัดสินใจใช้รูปแบบการเผยแพร่ / สมัครสมาชิกและใช้ความยาวต่อไปนี้หากเป็นเช่นเดียวกัน (FYI ฉันใช้jQuery เล็ก ๆ ผับ / ย่อย )

removeOrder = function(e, orders) {
    if (orders.length > 2) {
        orders.last().remove();
    }
}

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

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

ฉันคิดว่าประโยชน์ของ pub / sub จะทำให้ตัวเองชัดเจนในแอปพลิเคชันที่ซับซ้อนมากขึ้น แต่ฉันนึกไม่ออก ฉันกลัวว่าฉันจะพลาดประเด็นนี้ไปโดยสิ้นเชิง แต่ฉันอยากรู้ว่าถ้ามี!

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

คำตอบ:


222

ทุกอย่างเกี่ยวกับการมีเพศสัมพันธ์แบบหลวม ๆ และความรับผิดชอบเดียวซึ่งไปพร้อมกันกับรูปแบบ MV * (MVC / MVP / MVVM) ใน JavaScript ซึ่งทันสมัยมากในช่วงไม่กี่ปีที่ผ่านมา

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

เมื่อพูดถึงการมีเพศสัมพันธ์แบบหลวม ๆ เราควรพูดถึงการแยกความกังวล. หากคุณกำลังสร้างแอปพลิเคชันโดยใช้รูปแบบสถาปัตยกรรม MV * คุณจะมี Model (s) และ View (s) เสมอ โมเดลเป็นส่วนธุรกิจของแอปพลิเคชัน คุณสามารถใช้ซ้ำในแอปพลิเคชันต่างๆได้ดังนั้นจึงไม่ควรจับคู่กับมุมมองของแอปพลิเคชันเดียวที่คุณต้องการแสดงเพราะโดยปกติในแอปพลิเคชันต่างๆคุณจะมีมุมมองที่แตกต่างกัน ดังนั้นจึงควรใช้การเผยแพร่ / สมัครสมาชิกสำหรับการสื่อสาร Model-View เมื่อโมเดลของคุณเปลี่ยนแปลงโมเดลจะเผยแพร่เหตุการณ์มุมมองจะจับและอัปเดตตัวเอง คุณไม่มีค่าใช้จ่ายใด ๆ จากการเผยแพร่ / สมัครสมาชิกมันช่วยคุณในการแยกส่วน ในลักษณะเดียวกันคุณสามารถเก็บตรรกะของแอปพลิเคชันไว้ใน Controller ได้เช่น (MVVM, MVP ไม่ใช่ Controller) และทำให้ View เรียบง่ายที่สุด เมื่อมุมมองของคุณเปลี่ยนไป (หรือผู้ใช้คลิกที่บางสิ่งเป็นต้น) มันเพิ่งเผยแพร่เหตุการณ์ใหม่ผู้ควบคุมจะจับมันและตัดสินใจว่าจะทำอย่างไร หากคุณคุ้นเคยกับไฟล์MVCรูปแบบหรือกับMVVMในเทคโนโลยีของ Microsoft (WPF / Silverlight) คุณสามารถคิดของการเผยแพร่ / สมัครเช่นรูปแบบการสังเกตการณ์ แนวทางนี้ใช้ในเฟรมเวิร์กเช่น Backbone.js, Knockout.js (MVVM)

นี่คือตัวอย่าง:

//Model
function Book(name, isbn) {
    this.name = name;
    this.isbn = isbn;
}

function BookCollection(books) {
    this.books = books;
}

BookCollection.prototype.addBook = function (book) {
    this.books.push(book);
    $.publish('book-added', book);
    return book;
}

BookCollection.prototype.removeBook = function (book) {
   var removed;
   if (typeof book === 'number') {
       removed = this.books.splice(book, 1);
   }
   for (var i = 0; i < this.books.length; i += 1) {
      if (this.books[i] === book) {
          removed = this.books.splice(i, 1);
      }
   }
   $.publish('book-removed', removed);
   return removed;
}

//View
var BookListView = (function () {

   function removeBook(book) {
      $('#' + book.isbn).remove();
   }

   function addBook(book) {
      $('#bookList').append('<div id="' + book.isbn + '">' + book.name + '</div>');
   }

   return {
      init: function () {
         $.subscribe('book-removed', removeBook);
         $.subscribe('book-aded', addBook);
      }
   }
}());

ตัวอย่างอื่น. หากคุณไม่ชอบแนวทาง MV * คุณสามารถใช้สิ่งที่แตกต่างออกไปเล็กน้อย (มีจุดตัดระหว่างวิธีที่ฉันจะอธิบายต่อไปและที่กล่าวถึงสุดท้าย) เพียงจัดโครงสร้างแอปพลิเคชันของคุณในโมดูลต่างๆ ตัวอย่างเช่นดูที่ Twitter

โมดูล Twitter

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

นี่คือตัวอย่างพื้นฐานของแนวทางสุดท้าย (นี่ไม่ใช่รหัส twitter ดั้งเดิม แต่เป็นเพียงตัวอย่างของฉัน):

var Twitter.Timeline = (function () {
   var tweets = [];
   function publishTweet(tweet) {
      tweets.push(tweet);
      //publishing the tweet
   };
   return {
      init: function () {
         $.subscribe('tweet-posted', function (data) {
             publishTweet(data);
         });
      }
   };
}());


var Twitter.TweetPoster = (function () {
   return {
       init: function () {
           $('#postTweet').bind('click', function () {
               var tweet = $('#tweetInput').val();
               $.publish('tweet-posted', tweet);
           });
       }
   };
}());

สำหรับวิธีการนี้มีการพูดคุยที่ดีเยี่ยมโดยนิโคลัส Zakas สำหรับ MV * วิธีการบทความที่ดีที่สุดและหนังสือที่ฉันรู้ว่ามีการเผยแพร่โดยAddy Osmani

ข้อเสีย: คุณต้องระวังเกี่ยวกับการใช้งานเผยแพร่ / สมัครสมาชิกมากเกินไป หากคุณมีหลายร้อยเหตุการณ์อาจทำให้เกิดความสับสนในการจัดการทั้งหมด นอกจากนี้คุณยังอาจมีการชนกันหากคุณไม่ได้ใช้เนมสเปซ (หรือไม่ได้ใช้อย่างถูกวิธี) การดำเนินการขั้นสูงของคนกลางซึ่งมีลักษณะเหมือนเผยแพร่ / จองซื้อสามารถพบได้ที่นี่https://github.com/ajacksified/Mediator.js มีเนมสเปซและคุณสมบัติเช่นเหตุการณ์ "เดือด" ซึ่งแน่นอนว่าอาจถูกขัดจังหวะ ข้อเสียเปรียบอีกประการหนึ่งของการเผยแพร่ / สมัครสมาชิกคือการทดสอบหน่วยอย่างหนักอาจเป็นเรื่องยากที่จะแยกฟังก์ชั่นต่างๆในโมดูลและทดสอบอย่างอิสระ


3
ขอบคุณที่เข้าท่า ฉันคุ้นเคยกับรูปแบบ MVC เพราะฉันใช้มันตลอดเวลากับ PHP แต่ฉันไม่ได้คิดถึงมันในแง่ของการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์ :)
Maccath

2
ขอบคุณสำหรับคำอธิบายนี้ ช่วยให้ฉันเข้าใจแนวคิดนี้จริงๆ
flybear

1
นั่นคือคำตอบที่ดีเยี่ยม ไม่สามารถหยุดตัวเองโหวตสิ่งนี้ได้ :)
Naveed Butt

1
คำอธิบายที่ดีหลายตัวอย่างคำแนะนำในการอ่านเพิ่มเติม A ++
Carson

16

เป้าหมายหลักคือการลดการมีเพศสัมพันธ์ระหว่างรหัส เป็นวิธีคิดที่อิงตามเหตุการณ์ แต่ "เหตุการณ์" ไม่ได้ผูกติดอยู่กับวัตถุเฉพาะ

ฉันจะเขียนตัวอย่างใหญ่ ๆ ไว้ด้านล่างในรหัสหลอกที่ดูเหมือน JavaScript เล็กน้อย

สมมติว่าเรามีคลาสวิทยุและรีเลย์คลาส:

class Relay {
    function RelaySignal(signal) {
        //do something we don't care about right now
    }
}

class Radio {
    function ReceiveSignal(signal) {
        //how do I send this signal to other relays?
    }
}

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

class Radio {
    var relayList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function ReceiveSignal(signal) {
        for(relay in relayList) {
            relay.Relay(signal);
        }
    }

}

ใช้งานได้ดี แต่ตอนนี้ลองนึกดูว่าเราต้องการให้ส่วนประกอบอื่นเป็นส่วนหนึ่งของสัญญาณที่คลาสวิทยุได้รับ ได้แก่ Speakers:

(ขออภัยหากการเปรียบเทียบไม่ได้อยู่ในอันดับต้น ๆ ... )

class Speakers {
    function PlaySignal(signal) {
        //do something with the signal to create sounds
    }
}

เราสามารถทำซ้ำรูปแบบได้อีกครั้ง:

class Radio {
    var relayList = [];
    var speakerList = [];

    function AddRelay(relay) {
        relayList.add(relay);
    }

    function AddSpeaker(speaker) {
        speakerList.add(speaker)
    }

    function ReceiveSignal(signal) {

        for(relay in relayList) {
            relay.Relay(signal);
        }

        for(speaker in speakerList) {
            speaker.PlaySignal(signal);
        }

    }

}

เราสามารถปรับปรุงสิ่งนี้ให้ดียิ่งขึ้นได้ด้วยการสร้างอินเทอร์เฟซเช่น "SignalListener" เพื่อที่เราต้องการเพียงรายการเดียวในคลาส Radio และสามารถเรียกใช้ฟังก์ชันเดียวกันกับวัตถุใด ๆ ที่เรามีที่ต้องการฟังสัญญาณได้เสมอ แต่นั่นยังคงสร้างการมีเพศสัมพันธ์ระหว่างอินเทอร์เฟซ / คลาสพื้นฐาน / ฯลฯ ที่เราตัดสินใจและคลาสวิทยุ โดยพื้นฐานแล้วเมื่อใดก็ตามที่คุณเปลี่ยนคลาสวิทยุสัญญาณหรือรีเลย์คุณต้องคิดว่ามันจะส่งผลต่ออีกสองคลาสได้อย่างไร

ตอนนี้เรามาลองสิ่งที่แตกต่างกัน มาสร้างคลาสที่สี่ชื่อ RadioMast:

class RadioMast {

    var receivers = [];

    //this is the "subscribe"
    function RegisterReceivers(signaltype, receiverMethod) {
        //if no list for this type of signal exits, create it
        if(receivers[signaltype] == null) {
            receivers[signaltype] = [];
        }
        //add a subscriber to this signal type
        receivers[signaltype].add(receiverMethod);
    }

    //this is the "publish"
    function Broadcast(signaltype, signal) {
        //loop through all receivers for this type of signal
        //and call them with the signal
        for(receiverMethod in receivers[signaltype]) {
            receiverMethod(signal);
        }
    }
}

ตอนนี้เรามีไฟล์ รูปแบบที่เราตระหนักถึงและเราสามารถใช้กับจำนวนและประเภทของชั้นเรียนใดก็ได้ตราบเท่าที่:

  • ตระหนักถึง RadioMast (คลาสที่จัดการข้อความทั้งหมดที่ส่งผ่าน)
  • ตระหนักถึงลายเซ็นวิธีการในการส่ง / รับข้อความ

ดังนั้นเราจึงเปลี่ยนคลาสวิทยุเป็นรูปแบบสุดท้ายที่เรียบง่าย:

class Radio {
    function ReceiveSignal(signal) {
        RadioMast.Broadcast("specialradiosignal", signal);
    }
}

และเราเพิ่มลำโพงและรีเลย์ลงในรายการเครื่องรับของ RadioMast สำหรับสัญญาณประเภทนี้:

RadioMast.RegisterReceivers("specialradiosignal", speakers.PlaySignal);
RadioMast.RegisterReceivers("specialradiosignal", relay.RelaySignal);

ตอนนี้คลาส Speakers และ Relay ไม่มีความรู้อะไรเลยยกเว้นว่าพวกเขามีวิธีการที่สามารถรับสัญญาณได้และคลาส Radio ซึ่งเป็นผู้เผยแพร่รับรู้ถึง RadioMast ที่เผยแพร่สัญญาณไป นี่คือจุดสำคัญของการใช้ระบบส่งข้อความเช่นเผยแพร่ / สมัครสมาชิก


เยี่ยมมากที่มีตัวอย่างที่เป็นรูปธรรมที่แสดงให้เห็นว่าการนำรูปแบบ pub / sub ไปใช้จะดีกว่าการใช้วิธี 'ปกติ' อย่างไร! ขอบคุณ!
Maccath

1
ยินดีต้อนรับ! โดยส่วนตัวแล้วฉันมักพบว่าสมองของฉันไม่ 'คลิก' เมื่อพูดถึงรูปแบบ / วิธีการใหม่ ๆ จนกว่าฉันจะตระหนักถึงปัญหาที่แท้จริงที่สามารถแก้ไขได้สำหรับฉัน รูปแบบย่อย / ผับนั้นยอดเยี่ยมมากสำหรับสถาปัตยกรรมที่เชื่อมโยงกันอย่างแนบเนียน แต่เรายังคงต้องการแยกมันออกจากกันให้มากที่สุด ลองนึกภาพเกมที่คุณมีวัตถุหลายร้อยชิ้นที่ทุกคนต้องตอบสนองต่อสิ่งต่าง ๆ ที่เกิดขึ้นรอบตัวเช่นและวัตถุเหล่านี้สามารถเป็นได้ทุกอย่างไม่ว่าจะเป็นผู้เล่นสัญลักษณ์แสดงหัวข้อย่อยต้นไม้รูปทรงเรขาคณิตกุย ฯลฯ เป็นต้น
Anders Arpi

3
JavaScript ไม่มีclassคีย์เวิร์ด โปรดเน้นข้อเท็จจริงนี้เช่น โดยจำแนกรหัสของคุณเป็นรหัสหลอก
Rob W

จริงๆแล้วใน ES6 มีคีย์เวิร์ดของคลาส
Minko Gechev

5

คำตอบอื่น ๆ ทำได้ดีมากในการแสดงวิธีการทำงานของรูปแบบ ฉันต้องการที่จะตอบคำถามโดยนัย " อะไรที่ผิดปกติกับวิธีการเดิม? " ในขณะที่ฉันทำงานกับรูปแบบนี้เมื่อไม่นานมานี้และฉันพบว่ามันเกี่ยวข้องกับการเปลี่ยนความคิดของฉัน

ลองนึกภาพว่าเราสมัครรับข่าวสารทางเศรษฐกิจ แถลงการณ์เผยแพร่หัวข้อข่าว: " ลด Dow Jones ลง 200 คะแนน " นั่นจะเป็นข้อความที่แปลกและขาดความรับผิดชอบในการส่ง อย่างไรก็ตามหากมีการเผยแพร่: " Enron ยื่นขอความคุ้มครองจากการล้มละลายในบทที่ 11 เมื่อเช้านี้ " แสดงว่านี่เป็นข้อความที่มีประโยชน์มากกว่า โปรดทราบว่าข้อความอาจทำให้เกิด Dow Jones ตกลงไป 200 จุด แต่นั่นก็เป็นอีกเรื่องหนึ่ง

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

$.subscribe('iquery/action/remove-order', removeOrder);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order', $(this).parents('form:first').find('div.order'));
});

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

$.subscribe('iquery/action/remove-order-requested', handleRemoveOrderRequest);

$container.on('click', '.remove_order', function(event) {
    event.preventDefault();
    $.publish('iquery/action/remove-order-requested', $(this).parents('form:first').find('div.order'));
});

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

handleRemoveOrderRequest = function(e, orders) {
    logAction(e, "remove order requested");
    if( !isUserLoggedIn()) {
        adviseUser("You need to be logged in to remove orders");
    } else if (isOkToRemoveOrders(orders)) {
        orders.last().remove();
        adviseUser("Your last order has been removed");
        logAction(e, "order removed OK");
    } else {
        adviseUser("Your order was not removed");
        logAction(e, "order not removed");
    }
    remindUserToFloss();
    increaseProgrammerBrowniePoints();
    //etc...
}

ความแตกต่างระหว่างคำสั่งและการแจ้งเตือนเป็นความแตกต่างที่มีประโยชน์ในการสร้างรูปแบบนี้ IMO


หาก 2 ฟังก์ชันสุดท้าย ( remindUserToFloss& increaseProgrammerBrowniePoints) ของคุณอยู่ในโมดูลแยกกันคุณจะเผยแพร่ 2 เหตุการณ์ต่อกันที่นั่นทันทีhandleRemoveOrderRequestหรือคุณจะมีการflossModuleเผยแพร่เหตุการณ์ไปยังbrowniePointsโมดูลเมื่อremindUserToFloss()เสร็จสิ้น
Bryan P

4

เพื่อที่คุณจะได้ไม่ต้องฮาร์ดโค้ดการเรียกใช้ method / function คุณเพียงแค่เผยแพร่เหตุการณ์โดยไม่สนใจว่าใครจะฟัง สิ่งนี้ทำให้ผู้เผยแพร่เป็นอิสระจากผู้สมัครสมาชิกลดการพึ่งพา (หรือการมีเพศสัมพันธ์ตามคำที่คุณต้องการ) ระหว่าง 2 ส่วนต่างๆของแอปพลิเคชัน

นี่คือข้อเสียบางประการของการมีเพศสัมพันธ์ตามที่วิกิพีเดียกล่าวไว้

ระบบที่ทำงานร่วมกันอย่างแน่นหนามักจะแสดงลักษณะพัฒนาการดังต่อไปนี้ซึ่งมักถูกมองว่าเป็นข้อเสีย:

  1. การเปลี่ยนแปลงในโมดูลหนึ่งมักจะบังคับให้เกิดผลกระเพื่อมของการเปลี่ยนแปลงในโมดูลอื่น ๆ
  2. การประกอบโมดูลอาจต้องใช้ความพยายามและ / หรือเวลามากขึ้นเนื่องจากการพึ่งพาระหว่างโมดูลที่เพิ่มขึ้น
  3. โมดูลเฉพาะอาจใช้ซ้ำและ / หรือทดสอบได้ยากกว่าเนื่องจากต้องรวมโมดูลที่ต้องพึ่งพา

พิจารณาบางอย่างเช่นออบเจ็กต์ที่ห่อหุ้มข้อมูลทางธุรกิจ มีการเรียกวิธีฮาร์ดโค้ดเพื่ออัปเดตเพจเมื่อใดก็ตามที่กำหนดอายุ:

var person = {
    name: "John",
    age: 23,

    setAge: function( age ) {
        this.age = age;
        showAge( age );
    }
};

//Different module

function showAge( age ) {
    $("#age").text( age );
}

ตอนนี้ฉันไม่สามารถทดสอบวัตถุบุคคลโดยไม่รวมshowAgeฟังก์ชันด้วย นอกจากนี้หากฉันต้องการแสดงอายุในโมดูล GUI อื่นด้วยฉันจำเป็นต้องฮาร์ดโค้ดที่เรียกเมธอด .setAgeนั้นและตอนนี้มีการอ้างอิงสำหรับโมดูลที่ไม่เกี่ยวข้อง 2 โมดูลในวัตถุบุคคล นอกจากนี้ยังยากที่จะรักษาเมื่อคุณเห็นการโทรเหล่านั้นเกิดขึ้นและไม่ได้อยู่ในไฟล์เดียวกัน

โปรดทราบว่าภายในโมดูลเดียวกันคุณสามารถเรียกใช้เมธอดโดยตรงได้ แต่ข้อมูลทางธุรกิจและพฤติกรรมกุยแบบผิวเผินไม่ควรอยู่ในโมดูลเดียวกันตามมาตรฐานที่สมเหตุสมผล


ฉันไม่เข้าใจแนวคิดของ 'การพึ่งพา' ที่นี่ การพึ่งพาในตัวอย่างที่สองของฉันอยู่ที่ไหนและสิ่งที่สามของฉันหายไปไหน ฉันไม่เห็นความแตกต่างในทางปฏิบัติระหว่างส่วนย่อยที่สองและสามของฉัน - ดูเหมือนว่าจะเพิ่ม 'เลเยอร์' ใหม่ระหว่างฟังก์ชันและเหตุการณ์โดยไม่มีเหตุผลที่แท้จริง ฉันอาจจะตาบอด แต่ฉันคิดว่าฉันต้องการคำแนะนำมากกว่านี้ :(
Maccath

1
คุณช่วยระบุกรณีการใช้งานตัวอย่างที่การเผยแพร่ / สมัครสมาชิกจะเหมาะสมกว่าการสร้างฟังก์ชันที่ทำงานแบบเดียวกันได้หรือไม่?
Jeffrey Sweeney

@Maccath ใส่เพียง: ในตัวอย่างที่สามคุณไม่รู้หรือต้องรู้ว่า removeOrderมีอยู่จริงดังนั้นคุณจึงไม่สามารถพึ่งพามันได้ ในตัวอย่างที่สองคุณต้องรู้
Esailija

ในขณะที่ฉันยังรู้สึกว่ามีวิธีที่ดีกว่าในการอธิบายสิ่งที่คุณอธิบายไว้ที่นี่ แต่อย่างน้อยฉันก็เชื่อว่าวิธีการนี้มีจุดประสงค์โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมที่มีนักพัฒนาอื่น ๆ จำนวนมาก +1
Jeffrey Sweeney

1
@Esailija - ขอบคุณฉันคิดว่าฉันเข้าใจดีขึ้นเล็กน้อย ดังนั้น ... ถ้าฉันลบผู้สมัครสมาชิกทั้งหมดมันจะไม่ผิดพลาดหรืออะไรเลยมันจะไม่ทำอะไรเลย? และคุณจะบอกว่านี่อาจเป็นประโยชน์ในกรณีที่คุณต้องการดำเนินการ แต่ไม่จำเป็นต้องรู้ว่าฟังก์ชันใดมีความเกี่ยวข้องมากที่สุดในขณะเผยแพร่ แต่ผู้สมัครสมาชิกอาจเปลี่ยนแปลงได้โดยขึ้นอยู่กับปัจจัยอื่น ๆ
Maccath

1

การใช้งาน PubSub มักพบเห็นได้ทั่วไปในที่ที่มี -

  1. มีพอร์ตเล็ตเหมือนกับการนำไปใช้งานที่มีพอร์ตเล็ตหลายพอร์ตที่สื่อสารด้วยความช่วยเหลือของบัสเหตุการณ์ สิ่งนี้ช่วยในการสร้างในสถาปัตยกรรม aync
  2. ในระบบที่ถูกทำลายโดยการมีเพศสัมพันธ์ที่แน่น Pubsub เป็นกลไกที่ช่วยในการสื่อสารระหว่างโมดูลต่างๆ

โค้ดตัวอย่าง -

var pubSub = {};
(function(q) {

  var messages = [];

  q.subscribe = function(message, fn) {
    if (!messages[message]) {
      messages[message] = [];
    }
    messages[message].push(fn);
  }

  q.publish = function(message) {
    /* fetch all the subscribers and execute*/
    if (!messages[message]) {
      return false;
    } else {
      for (var message in messages) {
        for (var idx = 0; idx < messages[message].length; idx++) {
          if (messages[message][idx])
            messages[message][idx]();
        }
      }
    }
  }
})(pubSub);

pubSub.subscribe("event-A", function() {
  console.log('this is A');
});

pubSub.subscribe("event-A", function() {
  console.log('booyeah A');
});

pubSub.publish("event-A"); //executes the methods.

1

กระดาษ"The Many Faces of Publish / Subscribe"เป็นการอ่านที่ดีและสิ่งหนึ่งที่พวกเขาเน้นคือการแยกส่วน "มิติ" ออกเป็นสามส่วน นี่คือบทสรุปคร่าวๆของฉัน แต่โปรดอ้างอิงเอกสารด้วย

  1. การแยกพื้นที่ฝ่ายที่มีปฏิสัมพันธ์ไม่จำเป็นต้องรู้จักกัน สำนักพิมพ์ไม่ทราบว่าใครกำลังฟังอยู่มีคนฟังกี่คนหรือกำลังทำอะไรกับเหตุการณ์นี้ ผู้ติดตามไม่ทราบว่าใครเป็นผู้จัดทำกิจกรรมเหล่านี้มีผู้ผลิตกี่คน ฯลฯ
  2. การแยกเวลา ฝ่ายที่มีปฏิสัมพันธ์ไม่จำเป็นต้องใช้งานพร้อมกันในระหว่างการโต้ตอบ ตัวอย่างเช่นผู้สมัครสมาชิกอาจถูกตัดการเชื่อมต่อในขณะที่ผู้เผยแพร่กำลังเผยแพร่กิจกรรมบางอย่าง แต่สามารถตอบสนองได้เมื่อมีการออนไลน์
  3. การแยกการซิงโครไนซ์ ผู้เผยแพร่โฆษณาจะไม่ถูกปิดกั้นในขณะที่สร้างกิจกรรมและผู้ติดตามสามารถได้รับการแจ้งเตือนแบบอะซิงโครนัสผ่านการโทรกลับเมื่อใดก็ตามที่มีเหตุการณ์ที่พวกเขา subcribed มาถึง

0

คำตอบง่ายๆ เดิมต้องการคำตอบง่ายๆ นี่คือความพยายามของฉัน

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

ตอนนี้เราเห็นความต้องการรูปแบบ pub / sub แล้วคุณอยากจะจัดการเหตุการณ์ DOM ให้แตกต่างจากวิธีจัดการกับเหตุการณ์ pub / sub หรือไม่ เพื่อลดความซับซ้อนและแนวคิดอื่น ๆ เช่นการแยกข้อกังวล (SoC) คุณอาจเห็นประโยชน์ของทุกสิ่งที่เหมือนกัน

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

ฉันหวังว่าจะมีคนพบว่าการสนทนานี้ดีพอโดยไม่ต้องลงรายละเอียด

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