如何做正确的存储库工厂?

时间:2016-02-22 11:53:48

标签: angularjs typescript

我在项目之间有一些免费的日子,所以我决定学习typescript。 因此我想做一个存储库工厂。这个想法很简单,有一些API我可以访问models crud行为。对于buisnes模型,有一个generic repository是很好的解决方案。但仍然从model方法中获取CRUD类。

这样做的正确方法是什么?有人可以帮我解决这个问题吗?怎么做对了?

我想要的是:

var factory = new RepositoryFactory($resource, 'http://api.com');
var repo = factory.getRepository(User);
var user = repo.getAll();

我很想做这样的事:

IEntity

'use strict';

export interface IEntity {
    id: number;
}

IRepository

'use strict';

import {IEntity} from "./IEntity";

export interface IRepository<T extends IEntity> {
    getAll(params:Object): T[];
    getById(id:number): T;
    create(data:Object): T;
    update(id:number, data:{id:number}): T;
    remove(id:number): boolean;
}

RepositoryFactory

'use strict';

import {IEntity} from "./IEntity";
import {Repository} from "./Repository";

export default class RepositoryFactory {
    protected $resource:any;
    protected url:string;

    constructor($resource:any, url:string) {
        this.$resource = $resource;
        this.url = url;
    }

    public getRepository<T extends IEntity>(model:T):Repository {
        return new Repository(this.$resource, this.url, model)
    }
}

`Repository`:
'use strict';

import {IRepository} from "./IRepository";
import {IEntity} from "./IEntity";

export default class Repository<T extends IEntity> implements IRepository<T> {
    protected $resource:any;
    protected resource:any;
    protected url:string;
    protected model:T;

    constructor($resource:any, url:string, model:T) {
        this.$resource = $resource;
        this.url = url;
        this.model = model;
        this.resource = this.getResource(model.path);
    }

    getAll(params:Object):T[] {
        let results = this.resource.query((typeof params === 'undefined' ? {} : params), this.onSuccess);

        return this.returnPromise(results);
    }

    getById(id:number):T {
        let model = this.resource.get({id: id}, this.onSuccess);

        return this.returnPromise(model);
    }

    create(data:Object):T {
        let model = new this.resource(data);

        return model.$save().then(this.onSuccess);
    }

    update(id:number, data:Object):T {
        data.id = id;
        var model = new this.resource(data);

        return model.$update().then(this.onSuccess);
    }

    remove(id:number):boolean {
        var data = {id: id};
        var model = new this.resource(data);

        return model.$delete().then(this.onSuccess);
    }

    protected getResource(path:string) {
        return this.$resource(this.url + path, {id: '@id'}, {
            'update': {
                method: 'PUT'
            },
            'get': {
                method: 'GET'
            },
            'save': {
                method: 'POST'
            },
            'query': {
                method: 'GET'
            },
            'remove': {
                method: 'DELETE'
            },
            'delete': {
                method: 'DELETE'
            }
        });
    }

    protected onSuccess(response:any) {
        if (this.checkPropertyExistence(response, 'data')) {
            if (response.data instanceof Array) {
                let results = response.data;
                for (var key in results) {
                    if (results.hasOwnProperty(key)) {
                        results[key] = new this.model(results[key]);
                    }
                }

                return results;
            } else {
                return new this.model(response.data);
            }
        }

        return response;
    }

    protected transformRequest(obj:Object) {
        var str = [];
        for (var p in obj) {
            if (obj.hasOwnProperty(p)) {
                str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
            }
        }

        return str.join("&");
    }

    protected returnPromise(object:Object) {
        return object.$promise.then(function (result) {
            return result;
        });
    }

    protected checkPropertyExistence(obj:Object, paths:string|string[]) {
        for (var i = 0; i < paths.length; i++) {
            if (!obj || !obj.hasOwnProperty(paths[i])) {
                return false;
            }
            obj = obj[paths[i]];
        }
        return true;
    }
}

User

'use strict';

import {IEntity} from "./IEntity";

export default class User implements IEntity {
    id:number;
    name:string;

    static _path:string = '/users';

    constructor(id:number, name:string) {
        this.id = id;
        this.name = name;
    }

    static get path():string {
        return this._path;
    }
}

1 个答案:

答案 0 :(得分:2)

嘿伙计们,我有人为你创造工作榜样。 该示例显示了如何使用typescript创建存储库工厂。

我准备了这个DEMO FIDDLE,您可以在右侧按RUN,然后会出现带有单个按钮的页面。点击console.log按钮后,系统会显示getAll方法results

在示例中,我模拟了数据,只是为了表明工厂的工作原理。如果有人想改进它,请随意欢迎这样做!

它是如何工作的?

//create factory, you can do it in an abstract controller class
//and later extends controller by it so you can easy get access to repository
var factory = new RepositoryFactory();
//inject path for $resource (unused in example)
//and your model.entity namespace
var repo = factory.getRepository('/users', 'User');

//call repo method
var users = repo.getAll({});

使用角度创建RepositoryFactory作为service时。就这样。您可能还需要注入$resource以从API获取正确的数据。

这是一个完整的示例代码:

interface IEntity {
    id: number;
}

class Entity implements IEntity {
    private _id:number;
    private _name:string;
    private _birth:Date;

    constructor(parameters: {id:number, name:string, birth:Date}) {
        this._id = parameters.id;
        this._name = parameters.name;
        this._birth = parameters.birth;
    }

    get id():number {
        return this._id;
    }

    set id(value:number) {
        this._id = value;
    }

    get name():string {
        return this._name;
    }

    set name(value:string) {
        this._name = value;
    }

    get birth():Date {
        return this._birth;
    }

    set birth(value:Date) {
        this._birth = value;
    }
}

class RepositoryFactory {
    public getRepository<T extends IEntity>(path:string, model:string):IRepository<T> {
        return new Repository<T>(path, model)
    }
}

interface IRepository<T extends IEntity> {
    getAll(params:Object): T[];
    getById(id:number): T;
    create(data:Object): T;
    update(id:number, data:{id:number}): T;
    remove(id:number): boolean;
}

class Repository<T extends IEntity> implements IRepository<T> {
    protected path:string;
    protected model:string;

    constructor(path:string, model:string) {
        this.path = path;
        this.model = model;
    }

    getAll(params:Object):T[] {
        let results = [
            {id:1, name: 'rafal', birth:new Date()},
            {id:2, name: 'szymon', birth:new Date()},
            {id:3, name: 'mateusz', birth:new Date()},
        ];

        let entities= [];
        for (var key in results) {
            if (results.hasOwnProperty(key)) {
                let entity = Object.create(window[this.model].prototype);
                entity.constructor.apply(entity, new Array(results[key]));
                entities.push(entity);
            }
        }

        return entities;
    }

    getById(id:number):T {
        let object = {id:id, name: 'test', birth:new Date()};

        var entity = Object.create(window[this.model].prototype);
        entity.constructor.apply(entity, new Array(object));

        return entity;
    }

    create(data:Object):T {     
        var entity = Object.create(window[this.model].prototype);
        entity.constructor.apply(entity, new Array(data));

        return entity;
    }

    update(id:number, data:Object):T {
        var entity = Object.create(window[this.model].prototype);
        entity.constructor.apply(entity, new Array(data));

        return entity;
    }

    remove(id:number):boolean {
        return true;
    }
}

var factory = new RepositoryFactory();
var repo = factory.getRepository('/users', 'Entity');

var users = repo.getAll({});