Two implementations of the same interface and dynamic switching between them

Honza Kuchař
Backer | 1654
+
0
-

Hi!

I wanted to implement Basket into simple e-shop. There is need to dynamicaly switch between Basket model implementation based on if user is logged in.

So we currently have:

IBasketStorage
|- SessionBasketStorage
|- DatabaseBasketStorage

Implementations are registered in DI container, Presenter requires IBasketStorage. Where should I put that switching logic? (e.g.: When is logged in I need to inject DatabaseStorage and when it is not I need to inject SessionStorage)

Or should I use BasketStorageFactory? How it should look like? (probably one method with boolean parameter if is logged in or not, right?)

Filip Klimeš
Member | 157
+
+3
-

In my opinion, BasketStorageFactory is the best solution for this. The factory could require Nette\Security\User which knows whether the user is logged in (isLoggedIn() method). Actually, you could use Abstract Factory pattern and the BasketStorageFactory could require SessionBasketStorageFactory and DatabaseBasketStorageFactory (these could be generated atomagically by Nette), so that the BasketStorageFactory doesn't have to require all the dependencies for the other two factories.

class BasketStorageFactory {

    // .. constructor and attributes ..

    public function create()
    {
        if($this->user->isLoggedIn() {
            return $this->databaseBasketStorageFactory->create();
        } else {
            return $this->sessionBasketStorageFactory->create();
        }
    }

}

Last edited by Filip Klimeš (2015-03-05 12:20)

Comments

Honza Kuchař:

Finally we've used Service. So we were able to handle transitions from logged out state into logged in state and back.

<?php

class Service extends Object {
    // ... dependencies and costructor

    private $storage = null;

    /**
     * @return \App\Model\IStorage
     */
    public function getStorage() {
        if($this->storage === null){
            $this->storage = $this->createStorage();
        }
        return $this->storage;
    }

    private function createStorage() {
        return $this->user->loggedIn == true ? $this->databaseFactory->create() : $this->sessionFactory->create();
    }

    public function notifyUserLoggedIn() {
        // do migration when user had logged in
    }

    public function notifyUserLoggedOut() {
        // do migration when user had logged out
    }
}
5 years ago
David Matějka
Moderator | 5961
+
0
-

Or you can create some proxy class, e.g.

class BasketStorageProxy implements IBasketStorage
{
    //constructor..

    public function save(...)
    {
        if ($this->user->isLoggedIn()) {
            return $this->databaseBasketStorage->save(...);
        } else {
            return $this->sessionBasketStorage->save(...);
        }
    }
}

Comments

Honza Kuchař:

I think we do not need to mask underlying existence of storage class. So we've used Service class (as mentioned one post above)

5 years ago