มีหลักการ OO ใดบ้างที่ใช้ได้กับ Javascript จริงหรือไม่


79

Javascript เป็นภาษาเชิงวัตถุต้นแบบ แต่สามารถเป็นคลาสได้หลายวิธีโดย:

  • การเขียนฟังก์ชั่นที่จะใช้เป็นคลาสด้วยตัวเอง
  • ใช้ระบบระดับดีในกรอบ (เช่นmootools Class.Class )
  • สร้างจาก Coffeescript

ในตอนแรกฉันมักจะเขียนโค้ดตามคลาสใน Javascript และพึ่งพามันอย่างมาก อย่างไรก็ตามเมื่อเร็ว ๆ นี้ฉันได้ใช้กรอบงาน Javascript และNodeJSที่หายไปจากความคิดในชั้นเรียนนี้และพึ่งพาเพิ่มเติมเกี่ยวกับลักษณะแบบไดนามิกของรหัสเช่น:

  • การเขียนโปรแกรม Async การใช้และการเขียนโค้ดที่ใช้ callbacks / events
  • การโหลดโมดูลด้วย RequireJS (เพื่อไม่ให้รั่วไหลไปยัง namespace ทั่วโลก)
  • แนวคิดการเขียนโปรแกรมเชิงหน้าที่เช่นรายการความเข้าใจ (แผนที่ตัวกรองและอื่น ๆ )
  • เหนือสิ่งอื่นใด

สิ่งที่ฉันรวบรวมได้คือหลักการและรูปแบบ OO ส่วนใหญ่ที่ฉันอ่าน (เช่นรูปแบบ SOLID และ GoF) ถูกเขียนขึ้นสำหรับภาษา OO ที่อ้างอิงกับคลาสในใจเช่น Smalltalk และ C ++ แต่มีผู้ใดบ้างที่สามารถใช้งานได้กับภาษาต้นแบบเช่น Javascript หรือไม่

มีหลักการหรือรูปแบบที่เฉพาะเจาะจงกับ Javascript หรือไม่ หลักการในการหลีกเลี่ยงการเรียกกลับนรก , ความชั่วร้าย , หรือรูปแบบการต่อต้านอื่น ๆ

คำตอบ:


116

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

ก่อนอื่นeval()ไม่เลวเสมอไปและสามารถนำประโยชน์มาใช้ในการปฏิบัติงานเมื่อใช้ในการประเมินผลแบบขี้เกียจ การประเมินโดยสันหลังยาวคล้ายกับการโหลดสันหลังยาว แต่คุณเก็บรหัสไว้ในสตริงจากนั้นใช้evalหรือnew Functionเพื่อประเมินรหัส หากคุณใช้เล่ห์เหลี่ยมแล้วมันจะมีประโยชน์มากกว่าความชั่วร้าย แต่ถ้าคุณทำไม่ได้มันอาจนำไปสู่สิ่งเลวร้ายได้ คุณสามารถดูระบบโมดูลของฉันที่ใช้รูปแบบนี้: https://github.com/TheHydroImpulse/resolve.js Resolve.js ใช้ eval แทนที่จะnew Functionใช้โมเดล CommonJS exportsและmoduleตัวแปรที่มีอยู่ในแต่ละโมดูลเป็นหลักและnew Functionล้อมรอบโค้ดของคุณภายในฟังก์ชันที่ไม่ระบุชื่อแม้ว่าฉันจะปิดท้ายแต่ละโมดูลในฟังก์ชันที่ฉันทำด้วยตนเองร่วมกับ eval

คุณอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ในบทความสองบทความต่อไปนี้ในภายหลังยังอ้างถึงบทความแรก

เครื่องปั่นไฟ Harmony

ขณะนี้เครื่องกำเนิดไฟฟ้าได้ลงจอดในที่สุดใน V8 และใน Node.js ภายใต้ธง ( --harmonyหรือ--harmony-generators) สิ่งเหล่านี้ช่วยลดปริมาณการโทรกลับนรกของคุณได้อย่างมาก มันทำให้การเขียนโค้ดแบบอะซิงโครนัสนั้นยอดเยี่ยมอย่างแท้จริง

วิธีที่ดีที่สุดในการใช้เครื่องกำเนิดไฟฟ้าคือการใช้ไลบรารี่การควบคุมโฟลว์ สิ่งนี้จะช่วยให้การไหลของการดำเนินไปในขณะที่คุณให้กำเนิดภายในเครื่องกำเนิดไฟฟ้า

สรุป / ภาพรวม:

หากคุณไม่คุ้นเคยกับเครื่องกำเนิดไฟฟ้าพวกเขากำลังฝึกการหยุดการทำงานของฟังก์ชันพิเศษไว้ชั่วคราว (เรียกว่าเครื่องกำเนิดไฟฟ้า) การปฏิบัตินี้เรียกว่าการยอมให้ใช้yieldคำหลัก

ตัวอย่าง:

function* someGenerator() {
  yield []; // Pause the function and pass an empty array.
}

ดังนั้นเมื่อใดก็ตามที่คุณเรียกใช้ฟังก์ชันนี้ในครั้งแรกมันจะส่งคืนอินสแตนซ์ตัวสร้างใหม่ สิ่งนี้ช่วยให้คุณสามารถโทรหาnext()วัตถุนั้นเพื่อเริ่มหรือกลับสู่เครื่องกำเนิด

var gen = someGenerator();
gen.next(); // { value: Array[0], done: false }

คุณจะโทรnextต่อไปเรื่อย ๆ จนกว่าจะdoneกลับtrueมา ซึ่งหมายความว่าเครื่องกำเนิดไฟฟ้าทำงานเสร็จสมบูรณ์และไม่มีyieldข้อความเพิ่มเติมอีก

ควบคุมการไหล:

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

ตัวอย่าง:

var co = require('co');

co(function*() {
  yield query();
  yield query2();
  yield query3();
  render();
});

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

เครื่องกำเนิดไฟฟ้ายังใหม่อยู่และต้องใช้ Node.js> = v11.2 ขณะที่ฉันกำลังเขียนสิ่งนี้ v0.11.x ยังคงไม่เสถียรดังนั้นโมดูลเนทีฟจำนวนมากจึงไม่สามารถใช้งานได้และจะเป็นไปจนถึง v0.12 ซึ่ง API ดั้งเดิมจะสงบลง


หากต้องการเพิ่มคำตอบเดิมของฉัน:

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

ยกตัวอย่างเช่นระบบมุมมอง (ไคลเอนต์หรือเซิร์ฟเวอร์)

view('home.welcome');

ง่ายต่อการอ่านหรือติดตามมากกว่า:

var views = {};
views['home.welcome'] = new View('home.welcome');

viewฟังก์ชั่นก็จะตรวจสอบว่ามุมมองเดียวกันที่มีอยู่แล้วในแผนที่ในท้องถิ่น หากไม่มีมุมมองมุมมองก็จะสร้างมุมมองใหม่และเพิ่มรายการใหม่ลงในแผนที่

function view(name) {
  if (!name) // Throw an error

  if (view.views[name]) return view.views[name];

  return view.views[name] = new View({
    name: name
  });
}

// Local Map
view.views = {};

ขั้นพื้นฐานมากใช่มั้ย ฉันพบว่าอินเทอร์เฟซสาธารณะลดความซับซ้อนลงอย่างมากและทำให้ใช้งานง่ายขึ้น ฉันยังใช้ความสามารถด้านลูกโซ่ ...

view('home.welcome')
   .child('menus')
   .child('auth')

Tower, เฟรมเวิร์กที่ฉันกำลังพัฒนา (กับคนอื่น) หรือการพัฒนาเวอร์ชันถัดไป (0.5.0) จะใช้วิธีการทำงานนี้ในส่วนของการเปิดเผยอินเตอร์เฟส

บางคนใช้ประโยชน์จากเส้นใยเพื่อหลีกเลี่ยง "นรกเรียกกลับ" มันเป็นวิธีการที่แตกต่างจาก JavaScript และฉันไม่ใช่แฟนตัวยงของมัน แต่เฟรมเวิร์ก / แพลตฟอร์มจำนวนมากใช้ รวมถึง Meteor เนื่องจากถือว่า Node.js เป็นแพล็ตฟอร์มการเชื่อมต่อ / เธรด

ฉันควรใช้วิธีการที่เป็นนามธรรมเพื่อหลีกเลี่ยงการโทรกลับนรก มันอาจจะยุ่งยาก แต่มันลดความซับซ้อนของรหัสแอปพลิเคชันจริง เมื่อช่วยในการสร้างเฟรมเวิร์กTowerJSจะช่วยแก้ไขปัญหาของเราได้มากมาย แต่คุณจะยังคงมีการเรียกกลับในระดับหนึ่ง แต่การสร้างรังไม่ลึก

// app/config/server/routes.js
App.Router = Tower.Router.extend({
  root: Tower.Route.extend({
    route: '/',
    enter: function(context, next) {
      context.postsController.page(1).all(function(error, posts) {
        context.bootstrapData = {posts: posts};
        next();
      });
    },
    action: function(context, next) {
      context.response.render('index', context);
      next();
    },
    postRoutes: App.PostRoutes
  })
});

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

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

สำหรับรูปแบบใน JavaScript มันขึ้นอยู่กับความซื่อสัตย์ การสืบทอดมีประโยชน์จริง ๆ เมื่อใช้ CoffeeScript, Ember หรือกรอบ / โครงสร้างพื้นฐาน "คลาส" ใด ๆ เมื่อคุณอยู่ในสภาพแวดล้อม JavaScript ที่ "บริสุทธิ์" การใช้อินเทอร์เฟซต้นแบบแบบเดิมจะทำงานได้อย่างมีเสน่ห์:

function Controller() {
    this.resource = get('resource');
}

Controller.prototype.index = function(req, res, next) {
    next();
};

Ember.js เริ่มต้นสำหรับฉันอย่างน้อยก็ใช้วิธีการต่าง ๆ ในการสร้างวัตถุ แทนที่จะสร้างต้นแบบแต่ละวิธีอย่างอิสระคุณต้องใช้อินเทอร์เฟซที่คล้ายกับโมดูล

Ember.Controller.extend({
   index: function() {
      this.hello = 123;
   },
   constructor: function() {
      console.log(123);
   }
});

ทั้งหมดนี้คือสไตล์ "การเข้ารหัส" ที่แตกต่างกัน แต่เพิ่มไปยังฐานรหัสของคุณ

ความแตกต่าง

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

การออกแบบตามเหตุการณ์ / ส่วนประกอบ

โมเดลที่อิงกับเหตุการณ์และอิงตามองค์ประกอบนั้นเป็นผู้ชนะ IMO หรือวิธีที่ง่ายที่สุดในการทำงานโดยเฉพาะอย่างยิ่งเมื่อทำงานกับ Node.js ซึ่งมีส่วนประกอบ EventEmitter ในตัวแม้ว่าการใช้อิมิตเตอร์เหล่านี้เป็นเรื่องเล็กน้อย .

event.on("update", function(){
    this.component.ship.velocity = 0;
    event.emit("change.ship.velocity");
});

เป็นเพียงตัวอย่าง แต่เป็นแบบอย่างที่ดีที่ใช้งานได้ โดยเฉพาะอย่างยิ่งในโครงการที่มุ่งเน้นเกม / องค์ประกอบ

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

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

รูปแบบ Pub / Sub

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

model.subscribe("message", function(event){
    console.log(event.params.message);
});

model.publish("message", {message: "Hello, World"});

ผู้สังเกตการณ์

นี่อาจเป็นอัตนัยเนื่องจากบางคนเลือกที่จะคิดว่าแบบสังเกตการณ์เป็นแบบผับ / ย่อย แต่มีความแตกต่างกัน

"ผู้สังเกตการณ์เป็นรูปแบบการออกแบบที่วัตถุ (รู้จักกันในชื่อเรื่อง) จะเก็บรักษารายการของวัตถุโดยขึ้นอยู่กับมัน (ผู้สังเกตการณ์) โดยอัตโนมัติแจ้งให้ทราบถึงการเปลี่ยนแปลงใด ๆ ในสถานะ" - รูปแบบการสังเกตการณ์

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

ปฏิกิริยาการเขียนโปรแกรม

ปฏิกิริยาการเขียนโปรแกรมเป็นแนวคิดที่เล็กและไม่รู้จักมากขึ้นโดยเฉพาะใน JavaScript มีหนึ่งเฟรมเวิร์ก / ไลบรารี (ที่ฉันรู้) ซึ่งทำให้ง่ายต่อการทำงานกับ API เพื่อใช้ "การเขียนโปรแกรมปฏิกิริยา" นี้

ทรัพยากรเกี่ยวกับการเขียนโปรแกรมปฏิกิริยา:

โดยพื้นฐานแล้วมันมีชุดของการซิงค์ข้อมูล (ไม่ว่าจะเป็นตัวแปรฟังก์ชั่น ฯลฯ )

 var a = 1;
 var b = 2;
 var c = a + b;

 a = 2;

 console.log(c); // should output 4

ฉันเชื่อว่าการเขียนโปรแกรมแบบตอบโต้ถูกซ่อนไว้อย่างมากโดยเฉพาะอย่างยิ่งในภาษาที่จำเป็น มันเป็นกระบวนทัศน์การเขียนโปรแกรมที่ทรงพลังอย่างน่าอัศจรรย์โดยเฉพาะใน Node.js ดาวตกได้สร้างเป็นเอ็นจิ้นปฏิกิริยาตอบโต้ซึ่งเฟรมเวิร์กใช้ ปฏิกิริยาของ Meteor ทำงานอย่างไรหลังฉาก? เป็นภาพรวมที่ดีของวิธีการทำงานภายใน

Meteor.autosubscribe(function() {
   console.log("Hello " + Session.get("name"));
});

สิ่งนี้จะดำเนินการตามปกติแสดงค่าของnameแต่ถ้าเราเปลี่ยนมัน

Session.set ('name', 'Bob');

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

ดาวตกของ ...

รูปแบบปฏิกิริยาและรูปแบบสังเกตการณ์ค่อนข้างคล้ายกัน ความแตกต่างที่สำคัญคือรูปแบบของผู้สังเกตการณ์โดยทั่วไปจะอธิบายการไหลของข้อมูลกับทั้งวัตถุ / คลาสเทียบกับการเขียนโปรแกรมปฏิกิริยาอธิบายการไหลของข้อมูลไปยังคุณสมบัติที่เฉพาะเจาะจงแทน

ดาวตกเป็นตัวอย่างที่ดีของการเขียนโปรแกรมแบบโต้ตอบ รันไทม์มีความซับซ้อนเล็กน้อยเนื่องจากไม่มีเหตุการณ์การเปลี่ยนแปลงค่าดั้งเดิมของ JavaScript (ฮาร์โมนีพร็อกซีเปลี่ยนสิ่งนั้น) เฟรมเวิร์กฝั่งไคลเอ็นต์อื่นEmber.jsและAngularJSยังใช้การเขียนโปรแกรมแบบรีแอกทีฟ (เพื่อเพิ่มส่วนขยาย)

เฟรมเวิร์กสองโครงร่างที่ใหม่กว่าใช้รูปแบบปฏิกิริยาที่สะดุดตาที่สุดในเทมเพลต (อัพเดทอัตโนมัตินั่นคือ) Angular.js ใช้เทคนิคการตรวจสอบที่สกปรกง่าย ๆ ฉันจะไม่เรียกสิ่งนี้ว่าการเขียนโปรแกรมแบบโต้ตอบได้ แต่ก็ใกล้เพราะการตรวจสอบที่สกปรกไม่ใช่แบบเรียลไทม์ Ember.js ใช้วิธีการอื่น การใช้ Ember set()และget()วิธีการที่อนุญาตให้อัปเดตค่าต่าง ๆ ได้ทันที ด้วย runloop ของพวกเขามันมีประสิทธิภาพมากและช่วยให้ค่าขึ้นอยู่กับที่เชิงมุมมีข้อ จำกัด ทางทฤษฎี

สัญญา

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

fs.open("fs-promise.js", process.O_RDONLY).then(function(fd){
  return fs.read(fd, 4096);
}).then(function(args){
  util.puts(args[0]); // print the contents of the file
});

คุณสามารถกระจายฟังก์ชั่นการโทรกลับเพื่อไม่ให้อินไลน์ แต่นั่นเป็นการตัดสินใจออกแบบอีกครั้ง

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

ฟังก์ชั่นฟังก์ชั่นเดียว

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

ในที่สุดฉันขอแนะนำให้พัฒนาหรือใช้ "กรอบ" ขนาดเล็กโดยทั่วไปเป็นเพียงกระดูกสันหลังสำหรับใบสมัครของคุณและใช้เวลาในการสร้างบทคัดย่อตัดสินใจเกี่ยวกับระบบตามเหตุการณ์หรือโมดูลขนาดเล็กจำนวนมาก ระบบ "อิสระ" ฉันเคยทำงานกับหลายโครงการของ Node.js ซึ่งรหัสนั้นยุ่งมากกับ callback hell โดยเฉพาะ แต่ยังขาดความคิดก่อนที่จะเริ่มเขียนโค้ด ใช้เวลาในการคิดผ่านความเป็นไปได้ที่แตกต่างกันในแง่ของ API และไวยากรณ์

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

ผกผันของการควบคุม

แม้ว่าจะไม่เกี่ยวข้องกับ callback hell แต่ก็สามารถช่วยคุณในด้านสถาปัตยกรรมโดยรวมโดยเฉพาะในการทดสอบหน่วย

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

การผกผันของการควบคุมชนิดใด ๆ จะ decouple รหัสของคุณเป็นโมดูลแยกต่างหากที่สามารถล้อเลียนหรือปลอมแปลงได้ตลอดเวลา ออกแบบเอ็นจินการเรนเดอร์รุ่นที่สองของคุณหรือไม่ ยอดเยี่ยมเพียงใช้อินเทอร์เฟซเก่าแทนอันใหม่ ระบุตำแหน่งบริการเป็นที่น่าสนใจโดยเฉพาะอย่างยิ่งกับผู้รับมอบฉันทะ Harmony ใหม่ แต่เพียงได้อย่างมีประสิทธิภาพใช้งานได้ภายใน Node.js จะให้ดีกว่า API แล้วค่อนข้างใช้แทนService.get('render'); Service.renderฉันกำลังทำงานอยู่กับชนิดของระบบที่: https://github.com/TheHydroImpulse/Ettore

แม้ว่าการขาดการพิมพ์แบบคงที่ (การพิมพ์แบบคงที่เป็นเหตุผลที่เป็นไปได้สำหรับการใช้งานที่มีประสิทธิภาพในการฉีดพึ่งพาใน Java, C #, PHP - มันไม่ได้พิมพ์แบบคงที่ แต่มีคำใบ้ประเภท) อาจถูกมองว่าเป็นลบ ทำให้มันกลายเป็นจุดแข็งแน่นอน เนื่องจากทุกอย่างเป็นแบบไดนามิกคุณสามารถสร้างระบบแบบคงที่ "ปลอม" เมื่อใช้ร่วมกับ locator บริการคุณสามารถให้แต่ละองค์ประกอบ / โมดูล / คลาส / อินสแตนซ์เชื่อมโยงกับประเภท

var Service, componentA;

function Manager() {
  this.instances = {};
}

Manager.prototype.get = function(name) {
  return this.instances[name];
};

Manager.prototype.set = function(name, value) {
  this.instances[name] = value;
};

Service = new Manager();
componentA = {
  type: "ship",
  value: new Ship()
};

Service.set('componentA', componentA);

// DI
function World(ship) {
  if (ship === Service.matchType('ship', ship))
    this.ship = new ship();
  else
    throw Error("Wrong type passed.");
}

// Use Case:
var worldInstance = new World(Service.get('componentA'));

ตัวอย่างง่าย ๆ สำหรับโลกแห่งความเป็นจริงการใช้งานที่มีประสิทธิภาพคุณจะต้องนำแนวคิดนี้ไปใช้ต่อ คุณอาจต้องทำใจกับแนวคิดนี้เล็กน้อย ฉันไม่ได้คิดมากในตัวอย่างก่อนหน้านี้

Model-View-Controller

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

ตอนนี้มีสงครามกรอบ JavaScript ฝั่งไคลเอ็นต์ขนาดใหญ่ ส่วนใหญ่ใช้รูปแบบ MVC และพวกเขาทั้งหมดใช้มันแตกต่างกัน MVC ไม่ได้นำมาใช้เหมือนกันทุกครั้ง

หากคุณกำลังใช้อินเทอร์เฟซต้นแบบต้นแบบคุณอาจมีเวลายากที่จะได้รับน้ำตาลเชิงประโยคหรือ API ที่ดีเมื่อทำงานกับ MVC เว้นแต่ว่าคุณต้องการทำงานด้วยตนเอง Ember.js แก้ปัญหาด้วยการสร้างระบบ "class" / object "คอนโทรลเลอร์อาจมีลักษณะดังนี้:

 var Controller = Ember.Controller.extend({
      index: function() {
        // Do something....
      }
 });

ไลบรารีฝั่งไคลเอ็นต์ส่วนใหญ่ยังขยายรูปแบบ MVC โดยแนะนำตัวช่วยดู (กลายเป็นมุมมอง) และแม่แบบ (กลายเป็นมุมมอง)


คุณสมบัติใหม่ของ JavaScript:

สิ่งนี้จะมีผลก็ต่อเมื่อคุณใช้ Node.js แต่อย่างไรก็ตามมันมีค่ามาก การพูดคุยที่ NodeConf โดย Brendan Eichนำเสนอคุณสมบัติใหม่ที่ยอดเยี่ยม ไวยากรณ์ของฟังก์ชันที่เสนอและโดยเฉพาะอย่างยิ่งไลบรารีTask.js js

นี่อาจจะแก้ไขปัญหาส่วนใหญ่ด้วยการซ้อนฟังก์ชันและจะให้ประสิทธิภาพที่ดีขึ้นเล็กน้อยเนื่องจากไม่มีฟังก์ชั่นโอเวอร์เฮด

ผมไม่แน่ใจว่าเกินไปถ้า V8 สนับสนุนนี้กำเนิดสุดท้ายที่ผมตรวจสอบคุณจำเป็นต้องเปิดใช้งานธงบาง แต่งานนี้ในพอร์ตของ Node.js ที่ใช้แมงมุม

ทรัพยากรเพิ่มเติม:


2
เขียนดี ส่วนตัวแล้วฉันไม่มีประโยชน์ในการใช้ MV? ห้องสมุด เรามีทุกสิ่งที่เราจำเป็นต้องจัดระเบียบรหัสของเราสำหรับแอพที่มีความซับซ้อนมากขึ้น พวกเขาเตือนฉันมากเกี่ยวกับ Java และ C # ที่พยายามโยนอึม่านของตัวเองออกมามากมายในสิ่งที่เกิดขึ้นจริงในการสื่อสารกับเซิร์ฟเวอร์ เราได้รับ DOM เราได้รับมอบหมายงาน เราได้รับ OOP ฉันสามารถผูกกิจกรรมของตัวเองกับการเปลี่ยนแปลงข้อมูล tyvm
Erik Reppen

2
"แทนที่จะมีปัญหาการโทรกลับขนาดใหญ่ให้ใช้ฟังก์ชันเดียวกับงานเดี่ยวและทำงานนั้นให้ดี" - บทกวี
CuriousWebDeveloper

1
Javascript เมื่อถึงยุคมืดมากในช่วงต้นถึงกลางปี ​​2000 เมื่อมีน้อยคนจะเข้าใจวิธีการเขียนแอปพลิเคชันขนาดใหญ่โดยใช้ อย่างที่ @ErikReppen พูดว่าถ้าคุณพบว่าแอปพลิเคชัน JS ดูเหมือนแอปพลิเคชัน Java หรือ C # ที่คุณทำผิด
backpackcoder

3

การเพิ่มคำตอบแดเนียล:

ค่า / ส่วนประกอบที่สังเกตได้

ความคิดนี้ยืมมาจากกรอบ MVVM Knockout.JS ( ko.observable ) ด้วยความคิดที่ว่าคุณค่าและวัตถุสามารถเป็นเรื่องที่สังเกตได้และเมื่อการเปลี่ยนแปลงเกิดขึ้นในค่าเดียวหรือวัตถุมันจะอัพเดตผู้สังเกตการณ์ทั้งหมดโดยอัตโนมัติ โดยพื้นฐานแล้วมันเป็นรูปแบบการสังเกตการณ์ที่นำมาใช้ใน Javascript และแทนที่จะใช้เฟรมเวิร์ก pub / sub ส่วนใหญ่คีย์ "" จะเป็นหัวเรื่องแทนตัววัตถุเอง

การใช้งานมีดังนี้:

// the subjects
// plain old javascript object with observable values
var shipComponent = {
    velocity : observable(0)
};

// the observer, a player user interface
// implemented with revealing module pattern
var playerUi = (function(ship) {

  var module = {
    setVelocity: function (x) { 
      // ... sets the velocity on the player user interface
    },

    // only called once
    init: function() {

      // subscribe to changes on the velocity value
      // using the module's function as callback
      module.velocity.onChange(playerUi.setVelocity);
    }
  };

  return module;
})(shipComponent).init();

// the player ui will change when the velocity value is changed
shipComponent.velocity.set(10);

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

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

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

var observable = function(v) {
    var val = v, subscribers = [];

    // the observable object,
    // as revealing module
    var output = {

        // subscribes to event
        onChange : function(func) {
            // idiomatic JS to add object to the
            // subscribers array
            subscribers.push(func);

            return output: // enables chaining
        },

        // the method that changes the observable object
        // and emits the event
        set : function(v) {
            var i;
            val = v;
            for (i = 0, i < subscribers.length; i++) {
                // this is hardly fault tolerant but as long
                // as subscribers are functions it'll work
                subscribers[i](v);
            }

            return output;
        }

    };

    return output;
};

ฉันได้ใช้งานอ็อบเจกต์ที่สังเกตได้ใน JsFiddleซึ่งยังคงดำเนินต่อไปด้วยการสังเกตส่วนประกอบและความสามารถในการลบสมาชิก อย่าลังเลที่จะทดลอง JsFiddle

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