!!! Listings aus dem Artikel "Gleich geradeaus" von !!! Philip Ackermann in iX 3/2016, S. 100 !!! Listing 1:ES6-Klassensyntax 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; } } !!! Listing 2:Verwirrung durch Parameter // 1. klare Parameter let moritz = new Employee('Moritz', 'Mustermann', 4711); // 2. Unklare Bedeutung der Parameter let moritz = new Employee('Moritz', 'Mustermann', 4711, 180, true, false, true); // 3. Werte ganz ausgelassen let moritz = new Employee('Moritz', 'Mustermann', 4711, 180, null, null, true); !!! Listing 3:Erweiterte Klasse Person !!! oben: Person -- bitte kursiv class Person { constructor(firstName, lastName, age, weight, height) { this.firstName = firstName; this.lastName = lastName; this.age = age; this.weight = weight; this.height = height; } } !!! Listing 4:Person ändern heißt auch Employee ändern !!! oben: Person + Employee -- bitte kursi class Employee extends Person { constructor(firstName, lastName, age, weight, height, id) { super(firstName, lastName, age, weight, height); this.id = id; } } !!! Listing 5:Erste Version mit Konfigurationsobjekt class Person { constructor(config) { this.firstName = config.firstName; this.lastName = config.lastName; } } class Employee extends Person { constructor(config) { super(config); this.id = config.id; } } !!! Listing 6:Überprüfen/Neusetzen des Konfigurationsobjekts class Person { constructor(config) { config = typeof config === 'object' ? config : {}; this.firstName = config.firstName; this.lastName = config.lastName; } } class Employee extends Person { constructor(config) { config = typeof config === 'object' ? config : {}; super(config); this.id = config.id; } } !!! Listing 7:Überprüfen/Fehlerwerfen des Konfigurationsobjekts class Person { constructor(config) { if(typeof config !== 'object') { throw new TypeError('Kein Konfigurationsobjekt übergeben'); } this.firstName = config.firstName; this.lastName = config.lastName; } } class Employee extends Person { constructor(config) { if(typeof config !== 'object') { throw new TypeError('Kein Konfigurationsobjekt übergeben'); } super(config); this.id = config.id; } } !!! Listing 8:Standardwerte für einzelne Objekteigenschaften class Person { constructor(config) { config = typeof config === 'object' ? config : {}; this.firstName = config.firstName === undefined ? 'Max' : config.firstName; this.lastName = config.lastName === undefined ? 'Mustermann' : config.lastName; } } class Employee extends Person { constructor(config) { config = typeof config === 'object' ? config : {}; super(config); this.id = config.id === undefined ? 4711 : config.id; } } !!! Listing 9:Unit-Test fürs Refactoring describe('Constructor function', function() { it('should set the default values', function() { let moritz = new Employee('Moritz'); assert.equal(moritz.firstName, 'Moritz'); assert.equal(moritz.lastName, 'Mustermann'); assert.equal(moritz.id, 4711); }); }); !!! Listing 10:Eigenschaften aus dem Konfigurationsobjekt kopieren class Person { constructor(config) { config = typeof config === 'object' ? config : {}; this.firstName = 'Max'; this.lastName = 'Mustermann'; Object.assign(this, config); } } class Employee extends Person { constructor(config) { config = typeof config === 'object' ? config : {}; super(config); this.id = 4711; Object.assign(this, config); } } !!! Listing 11:Initialisierungscode in Methode auslagern class Person { constructor(config) { config = typeof config === 'object' ? config : {}; this.firstName = 'Max'; this.lastName = 'Mustermann'; this.init(config); } init(config) { Object.assign(this, config); } } class Employee extends Person { constructor(config) { config = typeof config === 'object' ? config : {}; super(config); this.id = 4711; super.init(config); } } !!! Listing 12:Elternkonstruktor vor Zugriff auf Objekteigenschafte class Employee extends Person { constructor(config) { config = typeof config === 'object' ? config : {}; this.id = 4711; // Aufruf nicht möglich, da this noch nicht initialisiert super(config); } } !!! Listing 13:Anwendung des Template-Method-Entwurfsmusters class Person { constructor(config) { // Template-Method Entwurfsmuster: this.initDefaults(); // Algorithmus Schritt 1 this.init(config); // Algorithmus Schritt 2 } init(config) { // Implementierung Schritt 1 config = typeof config === 'object' ? config : {}; Object.assign(this, config); } initDefaults() { // Implementierung Schritt 2 this.firstName = 'Max'; this.lastName = 'Mustermann'; } } class Employee extends Person { constructor(config) { super(config); } initDefaults() { // Implementierung Schritt 2 super.initDefaults(); this.id = 4711; } } !!! Listing 14:Extraktion der init()-Methode in Basisklasse class Base { constructor(config) { this.initDefaults(); this.init(config); } init(config) { config = typeof config === 'object' ? config : {}; Object.assign(this, config); } initDefaults() {} } class Person extends Base { constructor(config) { super(config); } initDefaults() { super.initDefaults(); this.firstName = 'Max'; this.lastName = 'Mustermann'; } } !!! Listing 15:Beliebige Eigenschaften über das Konfigurationsobjekt setzen describe('Constructor function', function() { it('should set the default values', function() { let moritz = new Employee({ firstName: 'Moritz', foo: 'bar' }); assert.equal(moritz.firstName, 'Moritz'); assert.equal(moritz.lastName, 'Mustermann'); assert.equal(moritz.id, 4711); assert.equal(moritz.foo, 'bar'); }); }); !!! Listing 16:Nur definierte Eigenschaften zulassen init(config) { if (typeof config === 'object') { Object.keys(this).forEach(key => { let configValue = config[key]; if (typeof configValue !== 'undefined' && configValue !== null) { if (typeof configValue === 'object') { if (this[key] === null) { this[key] = {}; } this[key] = Object.assign(this[key], configValue); } else { this[key] = configValue; } } }); } !!! Listing 17: Setter und Getter class Person extends Base { constructor(config) { super(config); } initDefaults() { super.initDefaults(); this.firstName = 'Max'; this.lastName = 'Mustermann'; } get firstName() { return this._firstName; } set firstName(firstName) { this._firstName = firstName; } /* Analog Getter und Setter für lastName */ } class Employee extends Person { constructor(config) { super(config); } initDefaults() { super.initDefaults(); this.id = 4711; } /* Analog Getter und Setter für id */ } !!! Listing 18: Angepasste init()-Methode init(config) { if (typeof config === 'object') { Object.keys(this).forEach(key => { key = key.substring(1); let configValue = config[key]; if (typeof configValue !== 'undefined' && configValue !== null) { if (typeof configValue === 'object') { if (this[key] === null) { this[key] = {}; } this[key] = Object.assign(this[key], configValue); } else { this[key] = configValue; } } }); }