टाइपस्क्रिप्ट: दो वर्गों का विस्तार कैसे करें?


85

मैं अपना समय बचाना चाहता हूं और PIXI कक्षाओं (एक 2d webGl रेंडरर लाइब्रेरी) तक फैली कक्षाओं में आम कोड का पुन: उपयोग करना चाहता हूं।

ऑब्जेक्ट इंटरफेस:

module Game.Core {
    export interface IObject {}

    export interface IManagedObject extends IObject{
        getKeyInManager(key: string): string;
        setKeyInManager(key: string): IObject;
    }
}

मेरा मुद्दा यह है कि कोड अंदर है getKeyInManagerऔर setKeyInManagerनहीं बदलेगा और मैं इसका पुन: उपयोग करना चाहता हूं, इसे डुप्लिकेट नहीं करना है, यहां कार्यान्वयन है:

export class ObjectThatShouldAlsoBeExtended{
    private _keyInManager: string;

    public getKeyInManager(key: string): string{
        return this._keyInManager;
    }

    public setKeyInManager(key: string): DisplayObject{
        this._keyInManager = key;
        return this;
    }
}

मैं जो करना चाहता हूं वह स्वचालित रूप से जोड़ना है, एक के माध्यम से Manager.add(), प्रबंधक का उपयोग वस्तु के अंदर वस्तु को अपनी संपत्ति में संदर्भित करने के लिए किया जाता है _keyInManager

तो, चलो एक बनावट के साथ एक उदाहरण लेते हैं। यहाँ जाता हैTextureManager

module Game.Managers {
    export class TextureManager extends Game.Managers.Manager {

        public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{
            return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name);
        }
    }
}

जब मैं करता हूं this.add(), तो मुझे उस Game.Managers.Manager add()विधि को कॉल करने की विधि चाहिए जो उस वस्तु पर मौजूद होगी जो उसके द्वारा लौटा दी गई है Game.Core.Texture.fromImage("/" + relativePath)। यह वस्तु, इस मामले में Texture:

module Game.Core {
    // I must extends PIXI.Texture, but I need to inject the methods in IManagedObject.
    export class Texture extends PIXI.Texture {

    }
}

मुझे पता है कि IManagedObjectएक इंटरफ़ेस है और इसमें कार्यान्वयन नहीं हो सकता है, लेकिन मुझे नहीं पता कि ObjectThatShouldAlsoBeExtendedमेरी Textureकक्षा के अंदर कक्षा को इंजेक्ट करने के लिए क्या लिखना है । यह जानते हुए कि एक ही प्रक्रिया के लिए आवश्यक होगा Sprite, TilingSprite, Layerऔर अधिक।

मुझे यहां एक अनुभवी टाइपस्क्रिप्ट फीडबैक / सलाह की आवश्यकता है, इसे करना संभव है, लेकिन एक से अधिक विस्तार से नहीं, क्योंकि उस समय केवल एक ही संभव है, मुझे कोई अन्य समाधान नहीं मिला।


7
बस एक टिप, जब भी मैं एक बहु विरासत समस्या में आता हूं, मैं खुद को "विरासत पर अनुकूल रचना" सोचने के लिए याद दिलाने की कोशिश करता हूं कि क्या वह काम करेगा।
बबलिंग

2
माना। 2 साल पहले इस तरह से नहीं सोच रहा था;)
वडॉरेक्वेस्ट

6
@ बड़बड़ाता है कि वंशानुक्रम से अधिक अनुकूल रचना यहां कैसे लागू होगी?
Seanny123

जवाबों:


94

टाइपस्क्रिप्ट में एक छोटी ज्ञात विशेषता है जो आपको मिक्सिन्स को फिर से उपयोग करने योग्य छोटी वस्तुओं को बनाने के लिए उपयोग करने की अनुमति देता है। आप कई इनहेरिटेंस का उपयोग करके बड़ी वस्तुओं में इनकी रचना कर सकते हैं (कई वंशानुक्रम वर्गों के लिए अनुमति नहीं है, लेकिन यह मिश्रण के लिए अनुमति दी जाती है - जो एक संबंधित निहितार्थ के साथ इंटरफेस की तरह हैं)।

टाइपस्क्रिप्ट मिक्सिन्स पर अधिक जानकारी

मुझे लगता है कि आप अपने खेल में कई वर्गों के बीच सामान्य घटकों को साझा करने के लिए और अपने खेल में एकल वर्ग से इनमें से कई घटकों का फिर से उपयोग करने के लिए इस तकनीक का उपयोग कर सकते हैं:

यहाँ एक त्वरित मिक्सिंस डेमो है ... सबसे पहले, फ्लेवर जिसे आप मिश्रण करना चाहते हैं:

class CanEat {
    public eat() {
        alert('Munch Munch.');
    }
}

class CanSleep {
    sleep() {
        alert('Zzzzzzz.');
    }
}

फिर मिक्सिन निर्माण के लिए जादू की विधि (आपको अपने कार्यक्रम में केवल एक बार इसकी आवश्यकता है ...)

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
             if (name !== 'constructor') {
                derivedCtor.prototype[name] = baseCtor.prototype[name];
            }
        });
    }); 
}

और फिर आप मिक्सिन फ्लेवर से कई विरासत के साथ कक्षाएं बना सकते हैं:

class Being implements CanEat, CanSleep {
        eat: () => void;
        sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);

ध्यान दें कि इस वर्ग में कोई वास्तविक कार्यान्वयन नहीं है - बस इसे "इंटरफेस" की आवश्यकताओं को पूरा करने के लिए पर्याप्त है। लेकिन जब हम इस वर्ग का उपयोग करते हैं - यह सब काम करता है।

var being = new Being();

// Zzzzzzz...
being.sleep();

3
यहां टाइपस्क्रिप्ट हैंडबुक में मिक्सिंस अनुभाग है (लेकिन स्टीव ने बहुत कुछ कवर किया है जो आपको इस उत्तर में जानने की जरूरत है, और उनके लिंक किए गए लेख में) typecriptlang.org/Handbook#mixins
Troy Gizzi

2
टाइपप्रति 2.2 अब mixins का समर्थन करता है
Flavien Volken

1
@FlavienVolken क्या आप जानते हैं कि Microsoft ने पुराने मिक्सिन्स सेक्शन को अपने हैंडबुक डॉक्यूमेंटेशन में क्यों रखा था? वैसे, मेरी तरह टीएस में शुरुआत के लिए रिलीज नोट्स को समझना मुश्किल है। टीएस 2.2+ मिक्सिन वाले ट्यूटोरियल के लिए कोई लिंक? धन्यवाद।
डेविड डी।

4
इस उदाहरण में दिखाए गए मिश्रण को करने का "पुराना तरीका" "नए तरीके" (टाइपस्क्रिप्ट 2.2+) की तुलना में सरल है। मुझे नहीं पता कि उन्होंने इसे इतना मुश्किल क्यों बनाया।
टोकेविले जूल

2
कारण "पुराना तरीका" प्रकार को सही ढंग से नहीं प्राप्त कर सकता है।

25

मैं वहां वर्णित नए मिश्रणों के दृष्टिकोण का उपयोग करने का सुझाव दूंगा: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/

फेंटन द्वारा वर्णित "अप्लाईमिक्सिन" दृष्टिकोण की तुलना में यह दृष्टिकोण बेहतर है, क्योंकि ऑटोकंपेलर आपको आधार और द्वितीय वंशानुक्रम वर्गों से सभी विधियों / गुणों को दिखाने में मदद करेगा।

इस दृष्टिकोण को टीएस प्लेग्राउंड साइट पर जांचा जा सकता है ।

यहाँ कार्यान्वयन है:

class MainClass {
    testMainClass() {
        alert("testMainClass");
    }
}

const addSecondInheritance = (BaseClass: { new(...args) }) => {
    return class extends BaseClass {
        testSecondInheritance() {
            alert("testSecondInheritance");
        }
    }
}

// Prepare the new class, which "inherits" 2 classes (MainClass and the cass declared in the addSecondInheritance method)
const SecondInheritanceClass = addSecondInheritance(MainClass);
// Create object from the new prepared class
const secondInheritanceObj = new SecondInheritanceClass();
secondInheritanceObj.testMainClass();
secondInheritanceObj.testSecondInheritance();

है SecondInheritanceClassउद्देश्य पर परिभाषित नहीं या मैं कुछ याद आ रही है? टीएस खेल के मैदान में इस कोड को लोड करते समय, यह कहता है expecting =>। अंत में, क्या आप addSecondInheritanceफंक्शन में क्या हो रहा है , जैसे कि इसका उद्देश्य क्या हो सकता है new (...args)?
Seanny123

ऐसे मिश्रणों के कार्यान्वयन का मुख्य बिंदु यह है कि दोनों वर्गों के सभी तरीके और गुण स्वत: पूर्ण आईडीई सहायता में दिखाए जाएंगे। यदि आप चाहें, तो आप द्वितीय श्रेणी को परिभाषित कर सकते हैं और फेंटन द्वारा सुझाए गए दृष्टिकोण का उपयोग कर सकते हैं, लेकिन इस मामले में आईडीई स्वतः पूर्ण काम नहीं करेगा। {नई (... args)} - इस कोड को एक वस्तु है जो एक वर्ग होना चाहिए का वर्णन करता है (यदि आप टीएस इंटरफेस के बारे में पुस्तिका में और अधिक पढ़ सकता है: typescriptlang.org/docs/handbook/interfaces.html
मार्क Dolbyrev

2
यहाँ समस्या यह है कि TS के पास अभी भी संशोधित वर्ग के बारे में कोई सुराग नहीं है। मैं टाइप कर सकता हूं secondInheritanceObj.some()और चेतावनी संदेश प्राप्त नहीं कर सकता।
एसएफ

अगर मिश्रित वर्ग इंटरफेस का पालन करता है तो कैसे जांचें?
rilut

11
टाइपस्क्रिप्ट से यह 'नया मिक्सिन्स एप्रोच' एक बाद की तरह दिखता है। एक डेवलपर के रूप में मैं सिर्फ यह कहना चाहता हूं कि "मैं इस वर्ग को ClassA और ClassB विरासत में देना चाहता हूं" या "मैं चाहता हूं कि यह वर्ग ClassA और ClassB का मिश्रण हो" और मैं स्पष्ट वाक्य रचना के साथ व्यक्त करना चाहता हूं जिसे मैं याद रख सकता हूं 6 महीने में, न कि मंबो जंबो। यदि यह टीएस संकलक की संभावनाओं का एक तकनीकी सीमा है तो यह हो सकता है, लेकिन यह कोई समाधान नहीं है।
रुई

12

दुर्भाग्य से टाइपस्क्रिप्ट एकाधिक वंशानुक्रम का समर्थन नहीं करता है। इसलिए पूरी तरह से तुच्छ जवाब नहीं है, आपको संभवतः अपने कार्यक्रम का पुनर्गठन करना होगा

यहां कुछ सुझाव दिए गए हैं:

  • यदि इस अतिरिक्त वर्ग में वह व्यवहार होता है जो आपके कई उपवर्ग साझा करते हैं, तो यह समझ में आता है कि इसे कक्षा पदानुक्रम में डाला जाए, कहीं शीर्ष पर। शायद आप इस वर्ग से स्प्राइट, बनावट, परत, ... के आम सुपरक्लास को प्राप्त कर सकते हैं? यह एक अच्छा विकल्प होगा, यदि आप प्रकार के पदानुक्रम में एक अच्छा स्थान पा सकते हैं। लेकिन मैं इस वर्ग को एक यादृच्छिक बिंदु पर सम्मिलित करने की सिफारिश नहीं करूंगा। वंशानुक्रम "इज ए - रिलेशनशिप" को व्यक्त करता है उदाहरण के लिए एक कुत्ता एक जानवर है, एक बनावट इस वर्ग का एक उदाहरण है। आपको अपने आप से पूछना होगा, अगर यह वास्तव में आपके कोड में वस्तुओं के बीच संबंध को मॉडल करता है। एक तार्किक वंशानुक्रम वृक्ष बहुत मूल्यवान है

  • यदि अतिरिक्त वर्ग तार्किक रूप से पदानुक्रम में फिट नहीं होता है, तो आप एकत्रीकरण का उपयोग कर सकते हैं। इसका मतलब है कि आप स्प्राइट, टेक्सचर, लेयर के एक सामान्य सुपरक्लास में इस वर्ग के प्रकार का एक इंस्टेंस वेरिएबल जोड़ते हैं, ... फिर आप सभी उपवर्गों में इसके गेटटर / सेटर के साथ वेरिएबल को एक्सेस कर सकते हैं। यह मॉडल "एक है - संबंध" है।

  • आप अपनी कक्षा को एक इंटरफ़ेस में भी बदल सकते हैं। तब आप अपने सभी वर्गों के साथ इंटरफ़ेस का विस्तार कर सकते थे, लेकिन प्रत्येक कक्षा में सही तरीके से तरीकों को लागू करना होगा। इसका मतलब है कुछ कोड अतिरेक लेकिन इस मामले में ज्यादा नहीं।

आपको अपने लिए तय करना होगा कि आपको कौन सा तरीका पसंद है। व्यक्तिगत रूप से मैं कक्षा को एक इंटरफ़ेस में बदलने की सिफारिश करूंगा।

एक टिप: टाइपस्क्रिप्ट गुण प्रदान करता है, जो गेटर्स और सेटर के लिए सिंटैक्टिक चीनी हैं। आप इसे देख सकते हैं: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/


2
दिलचस्प। 1) मैं ऐसा नहीं कर सकता, सिर्फ इसलिए कि मैं बढ़ाता PIXIहूं और मैं इसके ऊपर एक और वर्ग जोड़ने के लिए पुस्तकालय को नहीं बदल सकता। 2) यह संभव समाधानों में से एक है जिसका मैं उपयोग कर सकता हूं, लेकिन यदि संभव हो तो मैं इससे बचना पसंद करूंगा। 3) मैं निश्चित रूप से उस कोड को डुप्लिकेट नहीं करना चाहता, यह अब सरल हो सकता है, लेकिन आगे क्या होने वाला है? मैंने केवल एक दिन इस कार्यक्रम पर काम किया है और मैं बाद में बहुत कुछ जोड़ूंगा, मेरे लिए अच्छा समाधान नहीं। मैं टिप पर एक नज़र डालूँगा, अलग जवाब के लिए धन्यवाद।
वडकोरक्वेस्ट

दिलचस्प लिंक वास्तव में, लेकिन मुझे यहां समस्या को हल करने के लिए कोई उपयोग मामला नहीं दिखता है।
वडकोरक्वेस्ट

यदि ये सभी वर्ग PIXI का विस्तार करते हैं, तो बस ObjectThatShouldAlsoBeExtended वर्ग PIXI का विस्तार करें और उस से Texture, Sprite, ... कक्षाएं प्राप्त करें। यही कारण है कि मैं कक्षा में टाइप करने के साथ मतलब था hirarchy
lhk

PIXIखुद एक वर्ग नहीं है, यह एक मॉड्यूल है, इसे बढ़ाया नहीं जा सकता है, लेकिन आप सही हैं, अगर यह होता तो एक व्यवहार्य विकल्प होता!
वडकोरक्वेस्ट

1
तो प्रत्येक वर्ग PIXI मॉड्यूल से दूसरे वर्ग का विस्तार करता है? तब आप सही कह रहे हैं, आप ObjectThatShouldAlso वर्ग को hirarchy में सम्मिलित नहीं कर सकते। यह संभव नहीं है। आप अभी भी है-एक संबंध या इंटरफ़ेस चुन सकते हैं। चूंकि आप एक सामान्य व्यवहार का वर्णन करना चाहते हैं जो आपकी सभी कक्षाएं साझा करती हैं, इसलिए मैं एक इंटरफ़ेस का उपयोग करने की सलाह दूंगा। यह सबसे साफ डिज़ाइन है, भले ही कोड डुप्लिकेट हो।
lhk

10

टाइपस्क्रिप्ट डेकोरेटर का समर्थन करता है , और उस सुविधा का उपयोग करते हुए एक छोटी सी लाइब्रेरी जिसे टाइपस्क्रिप्ट-मिक्स कहा जाता है, आप सिर्फ एक दो लाइनों के साथ कई वंशानुक्रमों के मिश्रण का उपयोग कर सकते हैं

// The following line is only for intellisense to work
interface Shopperholic extends Buyer, Transportable {}

class Shopperholic {
  // The following line is where we "extend" from other 2 classes
  @use( Buyer, Transportable ) this 
  price = 2000;
}

7

मुझे लगता है कि बहुत बेहतर दृष्टिकोण है, जो ठोस प्रकार-सुरक्षा और मापनीयता के लिए अनुमति देता है।

पहले घोषणा करें कि आप अपने लक्ष्य वर्ग पर लागू करना चाहते हैं:

interface IBar {
  doBarThings(): void;
}

interface IBazz {
  doBazzThings(): void;
}

class Foo implements IBar, IBazz {}

अब हमें कार्यान्वयन को Fooवर्ग में जोड़ना होगा । हम वर्ग मिश्रणों का उपयोग कर सकते हैं जो इन इंटरफेस को भी लागू करते हैं:

class Base {}

type Constructor<I = Base> = new (...args: any[]) => I;

function Bar<T extends Constructor>(constructor: T = Base as any) {
  return class extends constructor implements IBar {
    public doBarThings() {
      console.log("Do bar!");
    }
  };
}

function Bazz<T extends Constructor>(constructor: T = Base as any) {
  return class extends constructor implements IBazz {
    public doBazzThings() {
      console.log("Do bazz!");
    }
  };
}

Fooवर्ग मिश्रण के साथ कक्षा बढ़ाएँ :

class Foo extends Bar(Bazz()) implements IBar, IBazz {
  public doBarThings() {
    super.doBarThings();
    console.log("Override mixin");
  }
}

const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin

बार और बेज़ के प्रकार क्या हैं?
ल्यूक स्काईवॉकर

3

एक बहुत ही घिनौना उपाय उस वर्ग के माध्यम से लूप होगा जिसे आप नए पैरेंट क्लास में एक-एक करके फ़ंक्शंस को जोड़ना चाहते हैं

class ChildA {
    public static x = 5
}

class ChildB {
    public static y = 6
}

class Parent {}

for (const property in ChildA) {
    Parent[property] = ChildA[property]
}
for (const property in ChildB) {
    Parent[property] = ChildB[property]
}


Parent.x
// 5
Parent.y
// 6

सब के सब गुण ChildAऔर ChildBअब से पहुँचा जा सकता Parentवर्ग है, लेकिन वे जिसका अर्थ है कि आप इस तरह के रूप में चेतावनी प्राप्त होगा नहीं पहचाना जाएगाProperty 'x' does not exist on 'typeof Parent'


1

डिजाइन पैटर्न में एक सिद्धांत है जिसे "विरासत पर रचना का पक्ष" कहा जाता है। यह कहता है कि क्लास ए से क्लास बी को विरासत में लेने के बजाय, क्लास ए के अंदर क्लास ए का एक प्रॉपर्टी के रूप में उदाहरण दें और फिर आप क्लास ए के वर्ग बी के कार्यात्मकता का उपयोग कर सकते हैं। आप इसके कुछ उदाहरण देख सकते हैं। यहां और यहां के



0

यहां पहले से ही बहुत सारे अच्छे उत्तर हैं, लेकिन मैं सिर्फ एक उदाहरण के साथ दिखाना चाहता हूं कि आप विस्तारित की जा रही कक्षा में अतिरिक्त कार्यक्षमता जोड़ सकते हैं;

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            if (name !== 'constructor') {
                derivedCtor.prototype[name] = baseCtor.prototype[name];
            }
        });
    });
}

class Class1 {
    doWork() {
        console.log('Working');
    }
}

class Class2 {
    sleep() {
        console.log('Sleeping');
    }
}

class FatClass implements Class1, Class2 {
    doWork: () => void = () => { };
    sleep: () => void = () => { };


    x: number = 23;
    private _z: number = 80;

    get z(): number {
        return this._z;
    }

    set z(newZ) {
        this._z = newZ;
    }

    saySomething(y: string) {
        console.log(`Just saying ${y}...`);
    }
}
applyMixins(FatClass, [Class1, Class2]);


let fatClass = new FatClass();

fatClass.doWork();
fatClass.saySomething("nothing");
console.log(fatClass.x);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.