簡體   English   中英

Angular 本地和 Session 可重用方法 設計模式

[英]Angular Local and Session reusable methods Design pattern

我將數據存儲在瀏覽器的本地和 Session 存儲中。 實施本地和 Session 存儲服務的良好設計實踐是什么? 我有一個用於處理 json 的通用服務。

@Injectable()
export class StorageService {
    private storage: any;

    constructor() {
        this.storage = sessionStorage;
    }

    public retrieve(key: string): any {
        const item = this.storage.getItem(key);

        if (item && item !== 'undefined') {
            return JSON.parse(this.storage.getItem(key));
        }

        return;
    }

    public store(key: string, value: any) {
        this.storage.setItem(key, JSON.stringify(value));
    }

    public remove(key: string) {
        this.storage.removeItem(key);
    }
}

如您所見,目前它與 Session 一起使用。 我還需要處理本地。

  1. 我可以使用這些功能制作一個抽象的父 class,並在子中提供本地或 Session 實例:LocalService、SessionService -> StorageService。
  2. 我可以為這些 json 函數添加一個參數,以便了解我使用的存儲。
public removeLocal() { .. }
public removeSession() { .. }

private remove(key: string, storage: Storage) {
    storage.removeItem(key);
}
  1. 如果您有其他更好的想法,請告訴我。 目前,我更喜歡變體 1,但我對一切都持開放態度。

這是可以使用策略模式的地方:

策略模式是一種行為軟件設計模式,可以在運行時選擇算法。 代碼不是直接實現單個算法,而是接收運行時指令,以確定要使用一系列算法中的哪一個。

讓我舉個例子。

我們需要有一些共同的行為,這些行為將在所有策略中共享。 在我們的例子中,它將是 session 或本地存儲的 CRUD 方法:

export interface Storage {    
    retrieve(key: string): string | null ;

    store(key: string, value: string): void;

    remove(key: string): void;
}

以及它的具體實現。 這些是可交換的策略:

export class LocalStorage implements Storage {

    retrieve(key: string): string | null {
        return localStorage.getItem(key)
    }

    store(key: string, value: string): void {
        localStorage.setItem(key, value);
    }

    remove(key: string): void {
        localStorage.removeItem(key);
    }
}

export class SessionStorage implements Storage {

    retrieve(key: string): string | null {
        return sessionStorage.getItem(key)
    }

    store(key: string, value: string): void {
        sessionStorage.setItem(key, value);
    }

    remove(key: string): void {
        sessionStorage.removeItem(key);
    }
}

這是一個 class 將執行策略:

export class StorageService {
    public storage: Storage;

    constructor(storage: Storage) {
        this.storage = storage;
    }

    retrieve(key: string): string | null {
        return this.storage.retrieve(key)
    }

    store(key: string, value: string): void {
        this.storage.store(key, value);
    }

    remove(key: string): void {
        this.storage.remove(key);
    }
}

然后我們可以這樣稱呼我們的策略:

const storage = new StorageService(new LocalStorage())
storage.store('some key', 'some value')

這種設計符合開閉原則 因此,如果您需要添加其他存儲,則:

  • 您將使用新策略添加新的 class
  • 您不會編輯StorageService class

並且符合開閉原則。

更新:

感謝Wiktor Zychla的評論:

客戶端仍然必須直接決定將哪個存儲傳遞給存儲服務。 每次客戶端需要存儲服務時,都需要通過一個具體的實現:new StorageService(new LocalStorage())。 向前邁出的一步是將 new LocalStorage() 隱藏在工廠 new LocalStorageFactory().Create() 后面,以便固定 API 調用,但可以在某處重新配置工廠,例如取決於配置。

是的,這是真的。 所以我們需要一個可以存儲所有策略的地方。 我們應該能夠從這家商店獲得必要的策略。 所以這是一個可以使用簡單工廠的地方。 簡單工廠不是工廠方法模式,也不是抽象工廠

export class StorageFactory {
    #storagesByKey : Record<string, Storage> = {
        'local': new LocalStorage(),
        'session': new SessionStorage(),
    }

    getInstanceByKey(key: string) {
        return this.#storagesByKey[key];
    }
}

然后您可以更輕松地獲得所需存儲的實例:

const storageFactory = new StorageFactory();
const storage = new StorageService(storageFactory.getInstanceByKey('local'))
storage.store('some key', 'some value')

我不會為此使用服務。 它可以是適合各種用途的簡單 class。

class MyStorage {
    constructor(
        private storage: Storage,
        private readonly prefix = '',
    ) {}

    private createKey(key: string): string {
        return this.prefix ? `${this.prefix}-${key}` : key;
    }

    public retrieve<T = any>(key: string): T {
        const item = this.storage.getItem(this.createKey(key));

        try {
            if (item && item !== 'undefined') {
                return JSON.parse(item);
            }
        } catch { }

        return;
    }

    public store<T = any>(key: string, value: T): void {
        this.storage.setItem(this.createKey(key), JSON.stringify(value));
    }

    public remove(key: string): void {
        this.storage.removeItem(this.createKey(key));
    }
}

這款的主要賣點是:

  • prefix - 稍后,當您在不同的地方多次使用它時,前綴將確保您沒有名稱沖突。
  • generics 是一種保護措施,因為您可以使用它們來驗證分配的值。
export const userSettingsSotrage = new MyStorage(localStorage, '[USER]');

userSettingsSotrage.retrieve<User>('user'); // Just a shorthand for "as User"
userSettingsSotrage.store<User>('user', userOrUndefined); // Error
userSettingsSotrage.store<User>('user', user); // OK

如果你想要更多的類型安全,你可以給整個MyStorage generics 來定義存在的鍵及其類型。 您甚至可以通過將值解析為您想要的特定 class 的方式來執行此操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM