มีวิธีสร้างอินเทอร์เฟซใน ES6 / Node 4 หรือไม่?


110

ES6 มีให้ใช้งานอย่างสมบูรณ์ในโหนด 4 ฉันสงสัยว่ามันมีแนวคิดของอินเทอร์เฟซเพื่อกำหนดสัญญาวิธีการMyClass implements MyInterfaceหรือไม่

ฉันหาข้อมูลเกี่ยวกับ Googling ไม่ได้มากนัก แต่อาจมีเคล็ดลับหรือวิธีแก้ปัญหาที่ดี


2
เต็มที่? ยังไม่.
Bergi

1
JS ยังคงใช้เป็ดพิมพ์ ไม่มี "สัญญาวิธีการ" ที่บังคับใช้แบบคงที่ หากคุณต้องการทดสอบแบบไดนามิกคุณสามารถเขียนตัวตรวจสอบอินเทอร์เฟซของคุณเองได้อย่างง่ายดาย
Bergi

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

9
หัวข้อนี้อยู่บนโลกได้อย่างไร? อินเทอร์เฟซเป็นเทคนิคการเขียนโปรแกรมไม่ใช่ผลิตภัณฑ์ คำถามนี้ถูกต้องและเป็นคำถามที่ดีเมื่อมีการเปิดตัว ECMA Script 6 ที่นำ Java มาใช้เช่นนิยามคลาส ฉันคิดว่าการปิดหัวข้อนี้แสดงให้เห็นถึงความไม่เข้าใจและวิธีการบน Stack ล้นระบบคะแนนไม่สัมพันธ์กับความสามารถ
Andrew S

4
OP (ขอ) เราแนะนำหรือหาหนังสือเครื่องมือห้องสมุดซอฟต์แวร์บทช่วยสอนหรือแหล่งข้อมูลนอกสถานที่อื่น ๆ ในคำถามนี้อย่างแท้จริง
Liam

คำตอบ:


90

อินเทอร์เฟซไม่ได้เป็นส่วนหนึ่งของ ES6 แต่เป็นคลาส

ถ้าคุณต้องการจริงๆพวกคุณควรดูที่typescriptที่สนับสนุนพวกเขา


1
"พวกเขา" เป็นอินเทอร์เฟซ FWIW คุณอาจต้องพิจารณาอย่างรอบคอบเกี่ยวกับลิงค์สำหรับทรานสไพเลอร์ที่ให้ไว้ด้านบน ไม่ตรงตามที่ฉันคาดไว้ แต่ใกล้เคียง

หมายเหตุ: เท่าที่ฉันทราบว่าอินเทอร์เฟซที่บริสุทธิ์ใน TypeScript จะไม่มีอะไรเลย เฉพาะในกรณีที่คุณใช้มันเท่านั้นดังนั้นรหัสที่โปร่งใสจะมีตรรกะบางอย่าง
Daniel Danielecki

9

ในความคิดเห็น debiasej เขียนบทความด้านล่างนี้อธิบายเพิ่มเติมเกี่ยวกับรูปแบบการออกแบบ (ตามอินเทอร์เฟซคลาส):

http://loredanacirstea.github.io/es6-design-patterns/

รูปแบบการออกแบบหนังสือในจาวาสคริปต์อาจเป็นประโยชน์สำหรับคุณ:

http://addyosmani.com/resources/essentialjsdesignpatterns/book/

รูปแบบการออกแบบ = คลาส + อินเทอร์เฟซหรือการสืบทอดหลาย ๆ

ตัวอย่างรูปแบบโรงงานใน ES6 JS (เพื่อรัน: node example.js):

"use strict";

// Types.js - Constructors used behind the scenes

// A constructor for defining new cars
class Car {
  constructor(options){
    console.log("Creating Car...\n");
    // some defaults
    this.doors = options.doors || 4;
    this.state = options.state || "brand new";
    this.color = options.color || "silver";
  }
}

// A constructor for defining new trucks
class Truck {
  constructor(options){
    console.log("Creating Truck...\n");
    this.state = options.state || "used";
    this.wheelSize = options.wheelSize || "large";
    this.color = options.color || "blue";
  }
}


// FactoryExample.js

// Define a skeleton vehicle factory
class VehicleFactory {}

// Define the prototypes and utilities for this factory

// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;

// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function ( options ) {

  switch(options.vehicleType){
    case "car":
      this.vehicleClass = Car;
      break;
    case "truck":
      this.vehicleClass = Truck;
      break;
    //defaults to VehicleFactory.prototype.vehicleClass (Car)
  }

  return new this.vehicleClass( options );

};

// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
            vehicleType: "car",
            color: "yellow",
            doors: 6 } );

// Test to confirm our car was created using the vehicleClass/prototype Car

// Outputs: true
console.log( car instanceof Car );

// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log( car );

var movingTruck = carFactory.createVehicle( {
                      vehicleType: "truck",
                      state: "like new",
                      color: "red",
                      wheelSize: "small" } );

// Test to confirm our truck was created with the vehicleClass/prototype Truck

// Outputs: true
console.log( movingTruck instanceof Truck );

// Outputs: Truck object of color "red", a "like new" state
// and a "small" wheelSize
console.log( movingTruck );

34
ที่นี่มีอินเทอร์เฟซที่ฉันสามารถเขียนร่วมกับผู้อื่นได้ที่ไหน?
Dmitri Zaitsev

คำอธิบายที่ลึกซึ้งยิ่งขึ้นอยู่ที่เว็บไซต์นี้: sitepoint.com/object-oriented-javascript-deep-dive-es6-classes
42n4

2
มีการปรับปรุงที่ดีของรูปแบบการ ES5 ES6 ที่เว็บไซต์นี้: loredanacirstea.github.io/es6-design-patterns
debiasej

8

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

ตอนนี้ไม่ได้หมายความว่าการจัดองค์ประกอบนั้นไม่อยู่ในคำถามอย่างไรก็ตามใน Plain Old JS ฉันค้นคว้าเรื่องนี้เมื่อนานมาแล้ว ผู้สมัครที่แข็งแกร่งที่สุดที่ฉันเคยเห็นในการจัดการองค์ประกอบภายในกระบวนทัศน์ต้นแบบวัตถุคือการประทับตราซึ่งตอนนี้ฉันใช้ในโครงการต่างๆมากมาย และที่สำคัญมันเป็นไปตามข้อกำหนดที่ชัดเจน

ข้อมูลเพิ่มเติมเกี่ยวกับแสตมป์ที่นี่


1
ฉันยืนอยู่ข้างโพสต์ของฉันแม้จะมี -1 น่าเศร้าที่บางครั้งนั่นก็คือประชาธิปไตยของ SO ฉันหวังว่าจะมีคนพบว่าลิงก์มีประโยชน์ Stampit คุ้มค่ากับเวลาของคุณ
Jay Edwards

-1 ไม่ใช่คำตัดสินสุดท้าย โพสต์ของคุณอาจจะ + 100 / -1 อย่างไรก็ตามฉันยังคิดว่ามันคลุมเครือ JS ไม่ใช่ "คลาสฟรี" อีกต่อไป ฉันสงสัยว่าส่วนใหญ่จะไม่เข้าใจคำว่า "การเรียบเรียงแบบคลาสสิก" ว่าหมายถึงสิ่งที่คุณหมายถึง: การถ่ายทอดทางพันธุกรรม (พิจารณามรดกทั้งหมดเทียบกับสงครามศักดิ์สิทธิ์) ยังไม่ชัดเจนว่า "Plain Old JS" คืออะไร ES5? แม้จะมีไวยากรณ์เพิ่มเติมอย่างละเอียดก็สนับสนุนเทคนิคที่แพร่หลายมากขึ้นในขณะนี้เช่น"จริง" ผสมอิน แสตมป์ดูน่าสนใจข้อดีของมันคืออะไร?
ᆼᆺᆼ

คีย์เวิร์ดของคลาสคือน้ำตาลวากยสัมพันธ์ JS - ES ^ 6 หรืออื่น ๆ - ไม่ใช่ภาษาคลาส เป็นเพียงการตกแต่งวิธีการสร้างฟังก์ชันแบบดั้งเดิมใน ES5 "JS เก่าธรรมดา" กำหนดการใช้งาน JS ของ ES อย่างมีความสุข ตรงไปตรงมาฉันหวังว่าการตัดสินใจไม่ได้เกิดขึ้นเพื่อฝังลึกความคิดของชั้นเรียนในภาษา quora.com/Are-ES6-classes-bad-for-JavaScript แสตมป์สะท้อนจุดแข็ง IMHO ของ JS ได้ดีกว่า stampit.js.orgให้สรุปความแตกต่างจากคลาส ท้ายที่สุดแล้วมันเป็นวิธีการที่เป็นประโยชน์มากกว่า
Jay Edwards

1
แต่แล้ว"ภาษาคลาส"คืออะไร? C ++? เป็นเพียงคำพ้องความหมายสำหรับclass structภาษาคลาสสิกอย่าง Smalltalk? มันจะช่วยให้การขยายแบบไดนามิกของต้นแบบและแม้กระทั่งกรณี
ᆼ ᆺ ᆼ

นั่นเป็นจุดที่สมเหตุสมผล ฉันจะกำหนดภาษาคลาสเป็นภาษาที่เป็น OOP ภายใน จาก MDN: "JavaScript เป็นรูปแบบที่ใช้ต้นแบบหลายกระบวนทัศน์ภาษาพลวัตสนับสนุนรูปแบบเชิงวัตถุจำเป็นและเชิงประกาศ (เช่นการเขียนโปรแกรมเชิงฟังก์ชัน)" google.com/url?sa=t&source=web&rct=j&url=https://…
Jay Edwards

6

นี่คือทางออกสำหรับปัญหาของฉัน คุณสามารถ 'ใช้งาน' อินเทอร์เฟซหลาย ๆ อินเทอร์เฟซได้โดยการแทนที่อินเทอร์เฟซหนึ่งกับอินเทอร์เฟซอื่น

class MyInterface {
    // Declare your JS doc in the Interface to make it acceable while writing the Class and for later inheritance
    /**
     * Gives the sum of the given Numbers
     * @param {Number} a The first Number
     * @param {Number} b The second Number
     * @return {Number} The sum of the Numbers
     */
    sum(a, b) { this._WARNING('sum(a, b)'); }


    // delcare a warning generator to notice if a method of the interface is not overridden
    // Needs the function name of the Interface method or any String that gives you a hint ;)
    _WARNING(fName='unknown method') {
        console.warn('WARNING! Function "'+fName+'" is not overridden in '+this.constructor.name);
    }
}

class MultipleInterfaces extends MyInterface {
    // this is used for "implement" multiple Interfaces at once
    /**
     * Gives the square of the given Number
     * @param {Number} a The Number
     * @return {Number} The square of the Numbers
     */
    square(a) { this._WARNING('square(a)'); }
}

class MyCorrectUsedClass extends MyInterface {
    // You can easy use the JS doc declared in the interface
    /** @inheritdoc */
    sum(a, b) {
        return a+b;
    }
}
class MyIncorrectUsedClass extends MyInterface {
    // not overriding the method sum(a, b)
}

class MyMultipleInterfacesClass extends MultipleInterfaces {
    // nothing overriden to show, that it still works
}


let working = new MyCorrectUsedClass();

let notWorking = new MyIncorrectUsedClass();

let multipleInterfacesInstance = new MyMultipleInterfacesClass();

// TEST IT

console.log('working.sum(1, 2) =', working.sum(1, 2));
// output: 'working.sum(1, 2) = 3'

console.log('notWorking.sum(1, 2) =', notWorking.sum(1, 2));
// output: 'notWorking.sum(1, 2) = undefined'
// but also sends a warn to the console with 'WARNING! Function "sum(a, b)" is not overridden in MyIncorrectUsedClass'

console.log('multipleInterfacesInstance.sum(1, 2) =', multipleInterfacesInstance.sum(1, 2));
// output: 'multipleInterfacesInstance.sum(1, 2) = undefined'
// console warn: 'WARNING! Function "sum(a, b)" is not overridden in MyMultipleInterfacesClass'

console.log('multipleInterfacesInstance.square(2) =', multipleInterfacesInstance.square(2));
// output: 'multipleInterfacesInstance.square(2) = undefined'
// console warn: 'WARNING! Function "square(a)" is not overridden in MyMultipleInterfacesClass'

แก้ไข:

ฉันปรับปรุงโค้ดเพื่อให้ตอนนี้คุณสามารถใช้งาน (baseClass, interface1, interface2, ... ) ในการขยาย

/**
* Implements any number of interfaces to a given class.
* @param cls The class you want to use
* @param interfaces Any amount of interfaces separated by comma
* @return The class cls exteded with all methods of all implemented interfaces
*/
function implement(cls, ...interfaces) {
    let clsPrototype = Object.getPrototypeOf(cls).prototype;
    for (let i = 0; i < interfaces.length; i++) {
        let proto = interfaces[i].prototype;
        for (let methodName of Object.getOwnPropertyNames(proto)) {
            if (methodName!== 'constructor')
                if (typeof proto[methodName] === 'function')
                    if (!clsPrototype[methodName]) {
                        console.warn('WARNING! "'+methodName+'" of Interface "'+interfaces[i].name+'" is not declared in class "'+cls.name+'"');
                        clsPrototype[methodName] = proto[methodName];
                    }
        }
    }
    return cls;
}

// Basic Interface to warn, whenever an not overridden method is used
class MyBaseInterface {
    // declare a warning generator to notice if a method of the interface is not overridden
    // Needs the function name of the Interface method or any String that gives you a hint ;)
    _WARNING(fName='unknown method') {
        console.warn('WARNING! Function "'+fName+'" is not overridden in '+this.constructor.name);
    }
}


// create a custom class
/* This is the simplest example but you could also use
*
*   class MyCustomClass1 extends implement(MyBaseInterface) {
*       foo() {return 66;}
*   }
*
*/
class MyCustomClass1 extends MyBaseInterface {
    foo() {return 66;}
}

// create a custom interface
class MyCustomInterface1 {
     // Declare your JS doc in the Interface to make it acceable while writing the Class and for later inheritance

    /**
     * Gives the sum of the given Numbers
     * @param {Number} a The first Number
     * @param {Number} b The second Number
     * @return {Number} The sum of the Numbers
     */
    sum(a, b) { this._WARNING('sum(a, b)'); }
}

// and another custom interface
class MyCustomInterface2 {
    /**
     * Gives the square of the given Number
     * @param {Number} a The Number
     * @return {Number} The square of the Numbers
     */
    square(a) { this._WARNING('square(a)'); }
}

// Extend your custom class even more and implement the custom interfaces
class AllInterfacesImplemented extends implement(MyCustomClass1, MyCustomInterface1, MyCustomInterface2) {
    /**
    * @inheritdoc
    */
    sum(a, b) { return a+b; }

    /**
    * Multiplies two Numbers
    * @param {Number} a The first Number
    * @param {Number} b The second Number
    * @return {Number}
    */
    multiply(a, b) {return a*b;}
}


// TEST IT

let x = new AllInterfacesImplemented();

console.log("x.foo() =", x.foo());
//output: 'x.foo() = 66'

console.log("x.square(2) =", x.square(2));
// output: 'x.square(2) = undefined
// console warn: 'WARNING! Function "square(a)" is not overridden in AllInterfacesImplemented'

console.log("x.sum(1, 2) =", x.sum(1, 2));
// output: 'x.sum(1, 2) = 3'

console.log("x.multiply(4, 5) =", x.multiply(4, 5));
// output: 'x.multiply(4, 5) = 20'

0

มีแพ็คเกจที่สามารถจำลองอินเทอร์เฟซได้

คุณสามารถใช้ es6-interface


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