Implementing the domain model

First of all, let's define our Genre enum. We'll use a string enum since this will be the easiest to understand:

enum Genre { 
    Horror = "Horror", 
    Fantastic = "Fantastic", 
    Thriller = "Thriller", 
    Romance = "Romance", 
    Fiction = "Fiction" 
} 

Next, we can implement the base Media class:

abstract class Media { 
    private _identifier: string; 
 
    protected constructor( 
        private _name: string, 
        private _description: string, 
        private _pictureLocation: string, 
        private _genre: Genre, 
        identifier?: string, 
    ) { 
        if (identifier) { 
            this._identifier = identifier; 
        } else { 
            // this is just for the example; for any real project, use 
            // UUIDs instead: https://www.npmjs.com/package/uuid 
            this._identifier = Math.random().toString(36).
substr(2,9); } } get identifier(): string { return this._identifier; } set identifier(identifier: string) { this._identifier = identifier; } get name(): string { return this._name; } set name(name: string) { this._name = name; } get description(): string { return this._description; } set description(description: string) { this._description = description; } get pictureLocation(): string { return this._pictureLocation; } set pictureLocation(pictureLocation: string) { this._pictureLocation = pictureLocation; } get genre(): Genre { return this._genre; } set genre(genre: Genre) { this._genre = genre; } }

Once again, we are using encapsulation. Additionally, we have marked this class as abstract since it doesn't make sense to be able to instantiate it.

The _identifier member will be used to acquire a somewhat unique technical key for each media instance. Note that you should use Universally Unique Identifiers (UUIDs) for this purpose in real applications instead, as the preceding approach is unsafe. We will do so in later chapters using the UUID library: https://www.npmjs.com/package/uuid.

Now we can implement the Book and Movie classes.

The following is the code for the Book class:

class Book extends Media { 
    private _author: string; 
    private _numberOfPages: number; 
 
    constructor( 
        name: string, 
        description: string, 
        pictureLocation: string, 
        genre: Genre, 
        author: string, 
        numberOfPages: number, 
        identifier?: string 
    ) { 
        super(name, description, pictureLocation, genre, identifier); 
        this._numberOfPages = numberOfPages; 
        this._author = author; 
    } 
 
    get author(): string { 
        return this._author; 
    } 
 
    set author(author: string) { 
        this._author = author; 
    } 
 
    get numberOfPages(): number { 
        return this._numberOfPages; 
    } 
 
    set numberOfPages(numberOfPages: number) { 
        this._numberOfPages = numberOfPages; 
    } 
} 
 

And the following is the Movie class: 

class Movie extends Media { 
    private _duration: string; 
    private _director: string; 
 
    constructor( 
        name: string, 
        description: string, 
        pictureLocation: string, 
        genre: Genre, 
        duration: string, 
        director: string, 
        identifier?: string 
    ) { 
        super(name, description, pictureLocation, genre, identifier); 
        this._duration = duration; 
        this._director = director; 
    } 
 
    get director(): string { 
        return this._director; 
    } 
 
    set director(director: string) { 
        this._director = director; 
    } 
 
    get duration(): string { 
        return this._duration; 
    } 
 
    set duration(duration: string) { 
        this._duration = duration; 
    } 
} 

Note that we optionally allow forcing an identifier. This will be useful when we retrieve persisted data, as we want those keys to remain stable (that is, they don't change).

Finally, we can also implement our MediaCollection class.

First of all, you need to add the class, private fields, and the constructor:

class MediaCollection<T extends Media> {     private _identifier: string; 
    private _name: string = ""; 
    private _collection: ReadonlyArray<T> = []; 
    private readonly _type: Function; 
 
    constructor( 
        type: Function, 
        name?: string, 
        identifier?: string 
    ) { 
        this._type = type; 
 
        if(name) { 
            this._name = name; 
        } 
 
        if (identifier) { 
            this._identifier = identifier; 
        } else { 
            // this is just for the example; for any real project, use 
            // UUIDs instead: https://www.npmjs.com/package/uuid 
            this._identifier = Math.random().toString(36).
substr(2, 9); } } }

Then add the methods:

    get identifier(): string { 
        return this._identifier; 
    } 
 
    set identifier(identifier: string) { 
        this._identifier = identifier; 
    } 
 
    get name(): string { 
        return this._name; 
    } 
 
    set name(name: string) { 
        this._name = name; 
    } 
 
    get collection(): ReadonlyArray<T> { 
        return this._collection; 
    } 
 
    set collection(collection: ReadonlyArray<T>) { 
        this._collection = collection; 
    } 
 
    addMedia(media: Readonly<T>): void { 
        if (media) { 
            this._collection = this._collection.concat(media); 
        } 
    } 
 
    removeMedia(itemId: string) { 
        if (itemId) { 
            this._collection = this._collection.filter(item => { 
                return item.identifier !== itemId; 
            }); 
        } 
    } 

As with the TodoIt application, we are using optional properties as well as read-only arrays with the ReadonlyArray type and generics. This code should already feel familiar to you.

We haven't marked properties as readonly for simplicity in terms of serialization/deserialization; readonly properties would have made the code more complex. For the same reason, we have also added setters for the different properties in all classes.

The idea with this design is that we will manipulate collections of different media types and the changes that the user makes will be transient, until they decide to hit the Save button. At that point, the collection will be persisted.

You should also note that this class accepts a generic type: <T extends Media>. This clearly states that instances of this class will hold instances of a class that extends from our abstract Media class. Thanks to that definition, both the collection array and the addMedia method can be made generic.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.75.227