Apakah ada cara untuk membuat antarmuka di ES6 / Node 4?

110

ES6 sepenuhnya tersedia di Node 4. Saya ingin tahu apakah ES6 menyertakan konsep antarmuka untuk mendefinisikan kontrak metode seperti pada MyClass implements MyInterface.

Saya tidak dapat menemukan banyak hal dengan Googling saya, tetapi mungkin ada trik atau solusi bagus yang tersedia.

Jérôme Verstrynge
sumber
2
Sepenuhnya? Sejauh ini tidak.
Bergi
1
JS masih menggunakan pengetikan bebek . Tidak ada "kontrak metode" yang diberlakukan secara statis. Jika Anda ingin mengujinya secara dinamis, Anda dapat dengan mudah menulis pemeriksa antarmuka Anda sendiri.
Bergi
26
Terlambat ke pesta, tetapi tidak setuju pertanyaannya di luar topik. OP menginginkan konfirmasi jika fitur yang diharapkan ada. Sintaks kelas yang baru dan disederhanakan sudah lama terlambat dan kemungkinan akan digunakan secara luas. Tetapi antarmuka umum dalam bahasa lain karena alasan yang sangat bagus. Saya juga terkejut, dan kecewa, mengetahui antarmuka bukan bagian dari ES2015. Mengingat bahwa ini kemungkinan merupakan penemuan umum, IMHO bukan tidak masuk akal untuk menanyakan apakah ada solusi yang disarankan.
9
Bagaimana mungkin ini di luar topik? Antarmuka adalah teknik pemrograman bukan produk. Pertanyaannya valid dan bagus dengan rilis ECMA Script 6 membawa definisi kelas seperti Java. Saya pikir penutup topik ini menunjukkan kurangnya pemahaman dan bagaimana Stack overflow sistem poin tidak berkorelasi dengan kemampuan.
Andrew S
4
Secara harfiah tidak ada gunanya OP (meminta) kami untuk merekomendasikan atau menemukan buku, alat, perpustakaan perangkat lunak, tutorial, atau sumber lain di luar situs dalam pertanyaan ini.
Liam

Jawaban:

90

Antarmuka bukan bagian dari ES6 tetapi kelas adalah.

Jika Anda benar-benar membutuhkannya, Anda harus melihat TypeScript yang mendukungnya .

gaelgillard
sumber
1
"mereka" menjadi antarmuka. FWIW Anda mungkin perlu mempertimbangkan dengan cermat link untuk transpiler yang disediakan di atas. Tidak persis seperti yang saya harapkan, tapi hampir.
Sebuah catatan: sejauh yang saya tahu antarmuka murni di TypeScript tidak transparan. Hanya jika Anda menggunakannya maka kode transpiled memiliki logika tertentu.
Daniel Danielecki
9

Dalam komentar debiasej menulis artikel yang disebutkan di bawah ini menjelaskan lebih lanjut tentang pola desain (berdasarkan antarmuka, kelas):

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

Buku pola desain dalam javascript mungkin juga berguna untuk Anda:

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

Pola desain = kelas + antarmuka atau beberapa warisan

Contoh pola pabrik di ES6 JS (untuk menjalankan: 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 );
42n4
sumber
34
Jadi di mana di sini antarmuka yang dapat saya buat dengan orang lain?
Dmitri Zaitsev
Beberapa penjelasan lebih dalam berada di situs ini: sitepoint.com/object-oriented-javascript-deep-dive-es6-classes
42n4
2
Ada pembaruan besar dari pola ES5 ke ES6 di situs ini: loredanacirstea.github.io/es6-design-patterns
debiasej
8

Mengingat ECMA adalah bahasa yang 'bebas kelas', menerapkan komposisi klasik tidak - menurut saya - masuk akal. Bahayanya adalah, dengan melakukan itu, Anda secara efektif mencoba merekayasa ulang bahasa (dan, jika seseorang merasa kuat tentang itu, ada solusi holistik yang sangat baik seperti TypeScript yang disebutkan di atas yang mengurangi penciptaan kembali roda)

Sekarang bukan berarti komposisi itu keluar dari pertanyaan namun di Plain Old JS. Saya meneliti ini panjang lebar beberapa waktu lalu. Kandidat terkuat yang pernah saya lihat untuk menangani komposisi dalam paradigma prototipe objek adalah stampit , yang sekarang saya gunakan di berbagai proyek. Dan, yang terpenting, ini mematuhi spesifikasi yang diartikulasikan dengan baik.

informasi lebih lanjut tentang perangko di sini

Jay Edwards
sumber
1
Saya berdiri di pos saya bahkan dengan -1. Sayangnya, terkadang itulah demokrasi SO. Saya harap seseorang menemukan tautannya bermanfaat. Stampit sangat berharga untuk waktumu.
Jay Edwards
-1 bukanlah putusan akhir. Pos Anda mungkin akan berakhir + 100 / -1. Namun saya masih berpikir itu tidak jelas. JS tidak lagi "bebas kelas". Saya curiga "Komposisi klasik" juga tidak akan dipahami oleh sebagian besar orang sebagai maksud Anda: warisan. (Pertimbangkan seluruh warisan vs komposisi perang suci.) Juga tidak jelas apa itu "JS Tua Biasa". ES5? Meskipun dengan sintaks yang lebih bertele-tele, teknik ini mendukung teknik yang lebih luas sekarang, seperti campuran yang "benar" . Perangko terlihat menarik, apa kelebihannya dibanding mix-in?
ᆼ ᆺ ᆼ
kata kunci kelasnya adalah gula sintaksis. JS - ES ^ 6 atau sebaliknya - bukan bahasa kelas. itu hanya menghiasi pendekatan konstruktor fungsi tradisional di ES5. "JS lama biasa" dengan senang hati mendefinisikan salah satu implementasi JS dari ES, oleh karena itu. Terus terang, saya berharap keputusan itu tidak dibuat untuk lebih memperkuat gagasan kelas dalam bahasa quora.com/Are-ES6-classes-bad-for-JavaScript. Perangko lebih mencerminkan kekuatan JS IMHO. stampit.js.org memberikan ikhtisar yang baik tentang perbedaan dari kelas. Pada akhirnya, ini adalah metodologi yang lebih pragmatis.
Jay Edwards
1
Tapi kemudian, apa itu "bahasa kelas" ? C ++? classhanyalah sinonim dari struct. Bahasa yang benar-benar klasik seperti Smalltalk? Hal ini memungkinkan perpanjangan dinamis dari prototipe dan bahkan instance
ᆼ ᆺ ᆼ
Itu poin yang masuk akal. Saya akan mendefinisikan bahasa kelas sebagai bahasa yang secara intrinsik OOP. Dari MDN: "JavaScript adalah berbasis prototipe, multi-paradigma, bahasa dinamis, mendukung gaya berorientasi objek, imperatif, dan deklaratif (misalnya pemrograman fungsional)." google.com/url?sa=t&source=web&rct=j&url=https://…
Jay Edwards
6

Ini adalah solusi saya untuk masalah tersebut. Anda dapat 'mengimplementasikan' banyak antarmuka dengan mengganti satu Antarmuka dengan yang lain.

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'

EDIT:

Saya meningkatkan kodenya sehingga Anda sekarang dapat menggunakan implement (baseClass, interface1, interface2, ...) dalam perpanjangan.

/**
* 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'
Kai Lehmann
sumber
0

ada paket yang dapat mensimulasikan antarmuka.

Anda dapat menggunakan es6-interface

Amit Wagner
sumber
2
Masalah dengan jawaban Anda adalah bahwa dia tidak meminta "alat" untuk melakukannya. tapi bagaimana itu dilakukan, queni akan menjadi jawaban yang lebih tepat yang menjelaskan bagaimana formulir itu melakukannya.
Gianfrancesco Aurecchia