JavaScript’teki kalıplar ve en iyi uygulamalar: Tip kontrolleri devam ediyor



  1. JavaScript’teki kalıplar ve en iyi uygulamalar: Tip kontrolleri devam ediyor

Bu serinin bir önceki makalesinde, örneğiJavaScript’teki -operator, bazı durumlarda, özellikle aynı “sınıfın” (veya prototipin) birden çok sürümüyle çalışırken sorunlu olabilir. Bu yazıda bu soruna geri dönmek ve bazı olası çözümleri özetlemek istiyorum.


Bir hatırlatma olarak, onu kullanırken bir sorun oluştu örneği Örneğin, aşağıda gösterildiği gibi Node.js altında, aynı modül dosyada birden çok kez görünür. düğüm_modülleridizin oluşur:

example-package // Hauptmodul
--> node_modules
--> package-a
--> package-b // package-b als Abhängigkeit von package-a ...
--> package-b // ... und als Abhängigkeit des Hauptmoduls.
--> package-c

Örneğin, tarafından oluşturulan paket-a bir sınıfın nesne örneği b paketisonra nesne örneğini iletin paket örneği ve orada tekrar kontrol et örneğibu kontrol yanlış bir sonuç verir (çünkü kontrol sınıfı belirtmez) paket-a > paket-b kullanılır, ancak kapalı b paketi üst düzeyde).


JavaScript’teki kalıplar ve en iyi uygulamalar




Bu arada, biraz daha genel yöntem burada da yardımcı olmuyor. Object.isPrototypeOf()prototipin izlenmesinden sorumlu olan (aksine örneği-operatör) test etmek için mutlaka bir yapıcı (veya yapıcı işlevi) gerektirmez, ancak prensipte herhangi bir nesneyi test edebilir.

Bu nedenle, aşağıdaki temel nesne modeline dayalı bazı çözüm yaklaşımlarını tartışmama izin verin. Unutmayın, bunlar aşağıda listelenen birim testlerini geçen, ancak pratikte henüz yeterince test edilmemiş fikirlerin eskizleridir (bu noktada duygu yorumlarına çok açığız).

'use strict';
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
class Employee extends Person {
constructor(firstName, lastName, id) {
super(firstName, lastName);
this.id = id;
}
}

Ayrıca bir yardımcı işlev içeren bir yardımcı sınıf tanımlıyoruz. ÖrnekOf(), daha sonra hangi çözüm yaklaşımlarının uygulandığı temelinde. Ancak başlangıçta, bu destek işlevi bundan yararlanır. örneği-Şebeke:


class TypeChecker {
static isInstanceOf(instance, clazz) {
return instance instanceof clazz;
}
}

Son olarak, uygulanmasını gösteren bir test durumu ÖrnekOf() Ölçek:

const mocha = require('mocha');
const assert = require('assert');
describe('TypeChecker', () => {
describe('isInstanceOf', () => {
it("should check for instances", () => {
let max = new Person('Max', 'Mustermann');
assert.equal(TypeChecker.isInstanceOf(max, Person), true);
assert.equal(TypeChecker.isInstanceOf(max, Employee), false);
let moritz = new Employee('Moritz', 'Mustermann', 4711);
assert.equal(TypeChecker.isInstanceOf(moritz, Person), true);
assert.equal(TypeChecker.isInstanceOf(moritz, Employee), true);
});
});
});

1. Çözüm: Sınıfları kimliğe göre benzersiz şekilde tanımlama


Buradaki fikir, sınıflara onları benzersiz bir şekilde tanımlayan yeni bir özellik vermek ve daha sonra bunları içeride kullanmaktır. ÖrnekOf() Kontrol. ES2015 ile dil standardına eklenen semboller özelliği açıklamak için kullanılabilir:

const IDENTIFIER = Symbol();
Person[IDENTIFIER] = '802e2228-2044-47c7-82b6-edbab38730d5';
Employee[IDENTIFIER] = 'c744bb74-9e3b-451f-84ab-57b9414227bf';

yöntemin içinde ÖrnekOf() nesnenin başlatıldığı sınıftaki bu özelliğin değerinin, test edilen sınıftaki özelliğin değeriyle eşleşip eşleşmediğini test etmek artık mümkün. Bunların hepsi, üst sınıfları da test etmek için tekrar tekrar yapılmalıdır (örneğin, nesne örneği moritz evet doğrudan bir örnek çalışanlarancak dolaylı olarak aynı zamanda sınıf hiyerarşisinde bir örnek kişi).

class TypeChecker {
static isInstanceOf(instance, clazz) {
let instancePrototype = Object.getPrototypeOf(instance);
let instanceClass = instancePrototype.constructor;
let isInstanceOf = instanceClass[IDENTIFIER] === clazz[IDENTIFIER];
let parentPrototype = Object.getPrototypeOf(instancePrototype);
let parentClass = parentPrototype.constructor;
if(!isInstanceOf) {
if(!(parentClass === Object)) {
isInstanceOf = TypeChecker.isInstanceOf(new parentClass(), clazz);
}
}
return isInstanceOf;
}
}

Bu yaklaşımın dezavantajı, açıkça, her şeyin çalışması için ilgili yazılım sistemindeki (tüm) sınıfların ek özellik ile genişletilmesi ve ayrıca değerlerin (ID’ler) benzersiz olduğunun garanti edilmesi gerektiğidir. . Bu, bu yaklaşımın hiçbir şekilde ölçeklenebilir bir yaklaşım olmadığı anlamına gelir.

2. Çözüm: Sınıf adını kontrol edin


Çözüm 1’de açıklandığı gibi sınıflara kimlik eklemek istemiyorsanız, bir nesnenin bir sınıfın örneği olup olmadığını kontrol etmenin (hatta) daha az güvenilir olsa da ikinci bir yolu vardır. Buradaki fikir, içinde olmaktır. ÖrnekOf()sınıf adını kontrol etme yöntemi (instanceClass.name === clazz.name).

class TypeChecker {
static isInstanceOf(instance, clazz) {
let instancePrototype = Object.getPrototypeOf(instance);
let instanceClass = instancePrototype.constructor;
let isInstanceOf = instanceClass.name === clazz.name;
let parentPrototype = Object.getPrototypeOf(instancePrototype);
let parentClass = parentPrototype.constructor;
if(!isInstanceOf) {
if(!(parentClass === Object)) {
isInstanceOf = TypeChecker.isInstanceOf(new parentClass(), clazz);
}
}
return isInstanceOf;
}
}

Bu yaklaşım, sınıfların kurcalanmasını engellerken, yalnızca sistemdeki her sınıfın benzersiz bir adı olduğu sürece çalışır. Bunu biraz daraltmak için, yöntem içinde (Node.js modülleri söz konusu olduğunda) yapabilirsiniz. ÖrnekOf() ayrıca ilgili modülün ad ve sürüm numarası gibi paket bilgilerine de erişin. Ancak o zaman bile, kendi modüllerinde iki sınıfın aynı ada sahip olmadığından emin olmanız gerekir.

3. Çözüm: Ördek yazarak


Ördek yazmanın arkasındaki fikir, bir nesne örneğinin somut türüne ve belirli bir sınıfla (veya yapıcı işlevle) yaratılıp yaratılmadığına değil, yalnızca nesne örneğinin sahip olduğu yöntemlere (veya özelliklere) dikkat etmektir. Aynı ismin sloganına sadık

“Ördek gibi yürüyen, ördek gibi yüzen ve ördek gibi ciyaklayan bir kuş gördüğümde, o kuşa ördek derim.”

Ördek yazarak, bir nesnenin türü dolaylı olarak yöntemleri/özellikleri tarafından belirlenir. Karşılık gelen bir uygulama ÖrnekOf() aşağıdaki listeyi gösterir:

const _ = require('underscore');
class TypeChecker {
static isInstanceOf(instance, clazz) {
let objectPropertyNames = Object.getOwnPropertyNames(instance);
let classPropertyNames = Object.getOwnPropertyNames(new clazz());
return classPropertyNames.length ===
_.intersection(classPropertyNames, objectPropertyNames).length;
}
}

Ne oluyor? Her şeyden önce, geçirilen nesne örneğinin tüm özelliklerinin adları ve geçirilen sınıfın (veya bir örneğinin) tüm özelliklerinin adları iki değişkende saklanır. nesneÖzellikAdları Ve sınıfÖzellikAdları kurtardı. Ardından (alt çizgi kitaplığı ve yönteminin yardımıyla kavşak ()) nesne örneğinin tüm özelliklerinin sınıf aracılığıyla oluşturulan örnekte de bulunup bulunmadığını kontrol etti. Bu durumda, nesne örneğinin aynı zamanda sınıfın bir örneği olduğu varsayılır.

4. Çözüm: Dosyanın üzerine yazın örneği-Davranış


Gerçekten dördüncü bir çözüm değil, daha ziyade şimdiye kadar açıklananların bir uzantısı, örneği-operatörler. Şu anda bu hala çok uzakta, ancak aşağıdaki kod zaten BabelJS JavaScript aktarıcısında çalıştırılabilir. bir çağrı örneği-ES2015’ten bu yana operatörler, özelliğe ait yöntemin dahili olarak çağrılmasına yol açar Symbol.hasInstance yatırılır. Dolayısıyla, bu (statik) yöntemi geçersiz kılarsanız, yöntemin davranışını değiştirebilirsiniz. örneği– Aşağıdaki listede gösterildiği gibi operatörler Tip Denetleyicisi erişim:

'use strict';
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
static [Symbol.hasInstance](instance) {
return TypeChecker.isInstanceOf(instance, this);
}
}
class Employee extends Person {
constructor(firstName, lastName, id) {
super(firstName, lastName);
this.id = id;
}
}
let max = new Person('Max', 'Mustermann');


doğrudan arandığında TypeChecker.isInstanceOf() artık birim testi içinde atlanabilir:

describe('TypeChecker', () => {
describe('isInstanceOf', () => {
it('should check for instances', () => {
let max = new Person('Max', 'Mustermann');
assert.equal(max instanceof Person, true);
assert.equal(max instanceof Employee, false);
let moritz = new Employee('Moritz', 'Mustermann', 4711);
assert.equal(moritz instanceof Person, true);
assert.equal(moritz instanceof Employee, true);
});
});
});;
Çözüm


Nesne örneklerinin tip denetimi, JavaScript’te kötü bir sorun olmaya devam ediyor. Çözüm 1, nesne modeli değişikliklerini zorlar ve ölçeklenmez, çözüm 2, nesne modeli değişikliği gerektirmez, ancak ölçeklenmez de, çözüm 3 – iyi bilinen ördek yazma ilkesi – yalnızca davranışı kontrol eder, ancak somut türü kontrol etmez (ki bu birçok durum yeterlidir) ve çözüm 4 zaten yalnızca diğer çözümleri kullanır.

Ama belki siz okuyucular, açıklanan sorunu uygun bir şekilde nasıl çözeceğiniz konusunda hala fikirleriniz var. Yoksa hepsini düzeltmeniz mi gerekiyor? Başka çözümleriniz var mı? Belki pratikte kendini kanıtlamış olanlar bile olabilir? Veya bunu kullan örneği-neredeyse operatör yok mu? Başka bir fikir, iç içe geçmiş Node.js modüllerinin orijinal sorununa çözüm bulmak ve bir proje içindeki tüm (yerel) modülleri dizin düzeyinde (genel modüllere benzer şekilde) yönetmek olabilir.


()



Haberin Sonu
 
Üst