ES6 / Düğüm 4'te arayüz oluşturmanın bir yolu var mı?


110

ES6, Düğüm 4'te tamamen kullanılabilir. Yöntem sözleşmelerini olduğu gibi tanımlamak için bir arayüz kavramı içerip içermediğini merak ediyordum MyClass implements MyInterface.

Googling'imle fazla bir şey bulamıyorum, ancak belki güzel bir numara veya geçici çözüm olabilir.



1
JS hala ördek yazmayı kullanıyor . Statik olarak uygulanan "yöntem sözleşmesi" yoktur. Bunları dinamik olarak test etmek istiyorsanız, kendi arayüz denetleyicinizi kolayca yazabilirsiniz.
Bergi

26
Partiye geç kaldım, ancak sorunun konu dışı olduğuna katılmıyorum. OP, beklenen bir özellik varsa onay ister. Sınıflar için yeni, basitleştirilmiş sözdiziminin gecikmesi çoktan geçti ve muhtemelen yaygın olarak kullanılacak. Ancak arayüzler diğer dillerde çok iyi sebeplerden dolayı yaygındır. Arayüzlerin ES2015'in bir parçası olmadığını öğrenince ben de şaşırdım ve hayal kırıklığına uğradım. Bunun muhtemelen ortak bir keşif olduğu göz önüne alındığında, IMHO'ya önerilen bir geçici çözüm olup olmadığını sormak mantıksız değildir.

9
Bu nasıl konu dışı? Arayüzler bir ürün değil programlama tekniğidir. Soru geçerlidir ve Java benzeri sınıf tanımlarını getiren ECMA Script 6 sürümüyle iyi bir sorudur. Bence bu konunun kapanışı anlayış eksikliğini ve Stack overflow'da puan sisteminin yetenek ile nasıl ilişkili olmadığını gösteriyor.
Andrew S

4
OP, kelimenin tam anlamıyla hiçbir noktada bizden bu sorunun herhangi birinde bir kitap, araç, yazılım kitaplığı, öğretici veya diğer site dışı kaynakları önermemizi veya bulmamızı istemez .
Liam

Yanıtlar:


90

Arayüzler ES6'nın bir parçası değildir ancak sınıflar vardır.

Eğer gerçekten ihtiyacınız varsa, bakmak gerekir typescript onlara destek .


1
"onlar" arayüzler. FWIW Yukarıda verilen aktarıcı bağlantısını dikkatlice düşünmeniz gerekebilir. Tam olarak beklediğim gibi değil ama yakın.

Bir not: Bildiğim kadarıyla, TypeScript aktarımlarında saf arayüz yok. Yalnızca bunları kullanırsanız, aktarılan kodun belirli bir mantığı vardır.
Daniel Danielecki

9

Yorumlarda debiasej, aşağıdaki makale tasarım kalıpları hakkında daha fazla bilgi verdiğini yazdı (arayüzlere, sınıflara göre):

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

JavaScript'teki tasarım kalıpları kitabı da sizin için yararlı olabilir:

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

Tasarım deseni = sınıflar + arayüz veya çoklu miras

ES6 JS'deki fabrika modeline bir örnek (çalıştırmak için: 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
Öyleyse burada başkalarıyla oluşturabileceğim bir arayüz nerede?
Dmitri Zaitsev

Bazı derin açıklamalar bu sitede yer almaktadır: sitepoint.com/object-oriented-javascript-deep-dive-es6-classes
42n4

2
Bu sitede ES5 kalıplarının ES6'ya harika bir güncellemesi var: loredanacirstea.github.io/es6-design-patterns
debiasej

8

ECMA'nın 'sınıfsız' bir dil olduğu düşünüldüğünde, klasik kompozisyonu uygulamak - benim gözümde - pek bir anlam ifade etmiyor. Tehlike, bunu yaparken, dili etkili bir şekilde yeniden yapılandırmaya çalışmanızdır (ve bu konuda güçlü bir his varsa, yukarıda bahsedilen TypeScript gibi tekerleği yeniden icat etmeyi azaltan mükemmel bütünsel çözümler vardır)

Ancak bu, Plain Old JS'de kompozisyonun söz konusu olmadığı anlamına gelmez. Bunu bir süre önce araştırdım. Nesne prototip paradigması içinde kompozisyonu işlemek için gördüğüm en güçlü aday Stampit , şu anda çok çeşitli projelerde kullandığım . Ve daha da önemlisi, iyi ifade edilmiş bir spesifikasyona bağlıdır.

pullar hakkında daha fazla bilgi burada


1
-1 ile bile yerimin arkasındayım. Ne yazık ki, bu bazen SO'nun demokrasisidir. Umarım birisi bağlantıları yararlı bulur. Stampit zaman ayırmaya değer.
Jay Edwards

-1 nihai bir karar değildir. Gönderiniz + 100 / -1 olabilir. Yine de bunun belirsiz olduğunu düşünüyorum. JS artık "sınıfsız" değil. "Klasik kompozisyon" un çoğu kişi tarafından kastettiğin şeyi kastettiğini anlamayacağından şüpheleniyorum: miras. (Bütün miras ve kompozisyon kutsal savaşını düşünün.) "Eski Eski JS" nin ne olduğu da net değil. ES5? Daha ayrıntılı bir sözdizimine rağmen, "gerçek" karışımlar gibi artık daha yaygın olan teknikleri destekledi . Pullar ilginç görünüyor, karışımlara göre avantajları nelerdir?
ᆼ ᆺ ᆼ

class anahtar sözcüğü sözdizimsel şekerdir. JS - ES ^ 6 veya başka türlü - bir sınıf dili değildir. ES5'teki geleneksel işlev yapıcı yaklaşımını süslüyor. "sade eski JS", ES'nin JS uygulamalarını mutlu bir şekilde tanımlar. Açıkçası, quora.com/Are-ES6-classes-bad-for-JavaScript dilinde sınıf fikrini daha da sağlamlaştırmak için kararın verilmemiş olmasını diliyorum. Pullar, JS'nin IMHO'nun güçlü yönlerini daha iyi yansıtıyor. stampit.js.org , sınıflardan farklılıkların iyi bir özetini verir. Sonuçta, daha pragmatik bir metodolojidir.
Jay Edwards

1
Ama o zaman "sınıf dili" nedir? C ++? classsadece eşanlamlıdır struct. Smalltalk gibi gerçekten klasik bir dil mi? Bu prototip ve hatta örnekleri dinamik uzantısını tanır
ᆼ ᆺ ᆼ

Bu makul bir nokta. Bir sınıf dilini, özünde OOP olan bir dil olarak tanımlardım. MDN'den: "JavaScript, prototip tabanlı, çok paradigmalı, dinamik bir dildir, nesne yönelimli, zorunlu ve bildirimsel (örneğin işlevsel programlama) stilleri destekler." google.com/url?sa=t&source=web&rct=j&url=https://…
Jay Edwards

6

Sorun için bu benim çözümüm. Bir Arayüzü diğeriyle geçersiz kılarak birden fazla arayüzü 'uygulayabilirsiniz'.

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'

DÜZENLE:

Kodu geliştirdim, böylelikle artık uzantı içinde uygulama (baseClass, interface1, interface2, ...) kullanabilirsiniz.

/**
* 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

arayüzleri simüle edebilen paketler var.

es6 arayüzünü kullanabilirsiniz


2
Cevabınızla ilgili sorun, onun bunu yapmak için bir "araç" istememiş olmasıdır. ama nasıl yapıldı, queni, bu formun bunu nasıl yaptığını açıklayacak bir cevap daha doğru olurdu.
Gianfrancesco Aurecchia
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.