206 votes

Différences entre les modèles Proxy et Decorator

Pouvez-vous nous expliquer quelle est la différence entre Proxy y Décorateur ?

La principale différence que je vois est que lorsque nous supposons que Proxy utilise composition y Décorateur utilise agrégation alors il semble clair qu'en utilisant de multiples (un ou plusieurs) Décorateurs vous pouvez modifier ou ajouter des fonctionnalités à une instance préexistante (décoration), alors que Proxy possède sa propre instance interne de la classe mandataire et lui délègue certaines fonctionnalités supplémentaires (comportement du mandataire).

La question est : est-ce que Proxy créé par l'agrégation est toujours Proxy ou plutôt Décorateur ? Est-il permis (par définition dans les schémas du GoF) de créer Proxy avec l'agrégation ?

6voto

Eugene Points 6271

J'ai mis du temps à comprendre ce réponse et ce qu'elle signifie vraiment. Quelques exemples devraient rendre les choses plus claires.

Proxy d'abord :

public interface Authorization {
    String getToken();
} 

Et :

// goes to the DB and gets a token for example
public class DBAuthorization implements Authorization {
    @Override
    public String getToken() {
        return "DB-Token";
    }
}

Et il y a un appel de ce Authorization une assez stupide :

class Caller {
    void authenticatedUserAction(Authorization authorization) {
        System.out.println("doing some action with : " + authorization.getToken());
    }
}

Rien d'inhabituel jusqu'à présent, non ? Obtenir un jeton d'un certain service, utiliser ce jeton. Maintenant, il faut ajouter une autre exigence à l'image, ajouter la journalisation : c'est-à-dire enregistrer le jeton à chaque fois. C'est simple dans ce cas, il suffit de créer un fichier Proxy :

public class LoggingDBAuthorization implements Authorization {

    private final DBAuthorization dbAuthorization = new DBAuthorization();

    @Override
    public String getToken() {
        String token = dbAuthorization.getToken();
        System.out.println("Got token : " + token);
        return token;
    }
}

Comment l'utiliserions-nous ?

public static void main(String[] args) {
    LoggingDBAuthorization loggingDBAuthorization = new LoggingDBAuthorization();

    Caller caller = new Caller();
    caller.authenticatedUserAction(loggingDBAuthorization);
}

Remarquez que LoggingDBAuthorization tient une instance de DBAuthorization . Les deux sites LoggingDBAuthorization y DBAuthorization mettre en œuvre Authorization .

  • Un proxy contient une implémentation concrète ( DBAuthorization ) de l'interface de base ( Authorization ). En d'autres termes, un Proxy sait exactement ce qui est proxié.

Decorator :

Ça commence à peu près de la même façon que Proxy avec une interface :

public interface JobSeeker {
    int interviewScore();
}

et une mise en œuvre de celle-ci :

class Newbie implements JobSeeker  {
    @Override
    public int interviewScore() {
        return 10;
    }
}

Et maintenant nous voulons ajouter un candidat plus expérimenté, qui ajoute son score d'entretien à celui d'un autre candidat. JobSeeker :

@RequiredArgsConstructor 
public class TwoYearsInTheIndustry implements JobSeeker {

    private final JobSeeker jobSeeker;

    @Override
    public int interviewScore() {
        return jobSeeker.interviewScore() + 20;
    } 
}

Remarquez comment j'ai dit que plus celui d'un autre JobSeeker , pas Newbie . A Decorator ne sait pas exactement ce qu'il décore, il ne connaît que le contrat de cette instance décorée (il connaît les éléments suivants JobSeeker ). Notez ici que ce n'est pas comme un Proxy qui, en revanche, sait exactement ce qu'il décore.

Vous pouvez vous demander s'il y a réellement une différence entre les deux modèles de conception dans ce cas ? Et si nous essayions d'écrire le Decorator en tant que Proxy ?

public class TwoYearsInTheIndustry implements JobSeeker {

    private final Newbie newbie = new Newbie();

    @Override
    public int interviewScore() {
        return newbie.interviewScore() + 20;
    }
}

Cette option est tout à fait envisageable et souligne à quel point ces motifs sont proches ; ils sont néanmoins destinés à des scénarios différents, comme expliqué dans les autres réponses.

5voto

James Lin Points 69

Le mandataire et le décorateur diffèrent par leur objectif et par l'endroit où ils se concentrent sur l'implémentation interne. Le proxy permet d'utiliser un objet distant, interprocessus ou interréseau comme s'il s'agissait d'un objet local. Le décorateur permet d'ajouter un nouveau comportement à l'interface originale.

Bien que les deux modèles aient une structure similaire, l'essentiel de la complexité du Proxy réside dans la garantie d'une communication correcte avec l'objet source. Le décorateur, quant à lui, se concentre sur l'implémentation du comportement ajouté.

3voto

Douma Points 488

A Decorator ajoute une responsabilité supplémentaire à un objet, tandis qu'un proxy contrôle l'accès à un objet, ils utilisent tous deux la composition. Si votre classe wrapper s'occupe de l'objet, il s'agit évidemment d'un proxy. Laissez-moi vous expliquer par un exemple de code en PHP :

Exemple de code

Voici ce qui est donné CarRepository :

interface CarRepositoryInterface 
{
    public function getById(int $id) : Car
}

class CarRepository implements CarRepositoryInterface 
{
    public function getById(int $id) : Car 
    {
        sleep(3); //... fake some heavy db call
        $car = new Car;
        $car->setId($id);
        $car->setName("Mercedes Benz");
        return $car;
    }
}

CarRepository- Proxy

A Proxy est souvent utilisé comme chargement paresseux ou comme proxy de cache :

class CarRepositoryCacheProxy implements CarRepositoryInterface 
{
    private $carRepository;

    private function getSubject() : CarRepositoryInterface
    {
        if($this->carRepository == null) {
            $this->carRepository = new CarRepository();
        }
        return $this->carRepository;
    }

    /**
     * This method controls the access to the subject
     * based on if there is cache available 
     */
    public function getById(int $id) : Car
    {
        if($this->hasCache(__METHOD__)) {
            return unserialize($this->getCache(__METHOD__));
        }   
        $response = $this->getSubject()->getById($id);
        $this->writeCache(__METHOD__, serialize($response));
        return $response;
    }

    private function hasCache(string $key) : bool 
    {
        //... implementation 
    }

    private function getCache(string $key) : string 
    {
        //... implementation 
    }

    private function writeCache(string $key, string $result) : string 
    {
        //... implementation 
    }
}

CarRepository- Decorator

A Decorator peut être utilisé tant que le comportement ajouté ne "contrôle" pas le sujet :

class CarRepositoryEventManagerDecorator implements CarRepositoryInterface 
{
    private $subject, $eventManager;

    /**
     * Subjects in decorators are often passed in the constructor, 
     * where a proxy often takes control over the invocation behavior 
     * somewhere else 
     */
    public function __construct(CarRepositoryInterface $subject, EventManager $eventManager)
    {
        $this->subject = $subject;
        $this->eventManager = $eventManager;
    }

    public function getById(int $id) : Car 
    {
        $this->eventManager->trigger("pre.getById");
        //this method takes no control over the subject
        $result = $this->subject->getById($id);
        $this->eventManager->trigger("post.getById");
        return $result;
    }
}

0voto

Ali Bayat Points 1331

Proxy fournit la même interface à l'objet enveloppé, Décorateur lui fournit une interface améliorée, et Proxy gère généralement le cycle de vie de son objet de service par lui-même, alors que la composition des décorateurs est toujours contrôlée par le client.

0voto

Gehooa Liou Points 23

Je vais d'abord expliquer les modèles, puis je répondrai à vos questions.

D'après le diagramme de classe et les significations, ils sont très similaires :

  1. Tous deux ont la même interface que celle de son délégataire.
  2. Les deux ajoutent/améliorent le comportement de son délégataire.
  3. Les deux demandent au délégataire d'effectuer des opérations (ne devrait pas fonctionner avec un délégataire nul).

Mais ils ont une certaine différence :

  1. Des intentions différentes : Le proxy améliore le comportement du délégué (objet passé) avec une connaissance du domaine très différente de celle de son délégué. Par exemple, un proxy de sécurité ajoute le contrôle de sécurité du délégué. Un proxy pour envoyer un message à distance a besoin de sérialiser/désérialiser les données et a des connaissances sur l'interface réseau, mais n'a rien à voir avec la façon de préparer les données sources. Le décorateur aide sur le même domaine de problème sur lequel le délégué travaille. Par exemple, BufferedInputStreaman (un décorateur IO) travaille sur l'entrée, qui est le même domaine problématique (IO) que son délégué, mais il ne peut pas fonctionner sans un délégué qui fournit des données IO.

  2. La dépendance est forte ou non : Le décorateur s'appuie sur le délégué pour terminer le comportement, et il ne peut pas terminer le comportement sans le délégué (fort). Ainsi, nous utilisons toujours l'agrégation plutôt que la composition. Le proxy peut exécuter un comportement simulé même s'il n'a pas besoin d'un délégué (faible). Par exemple, mockito (cadre de test unitaire) peut simuler/espionner un comportement juste avec son interface. Ainsi, nous utilisons la composition pour indiquer qu'il n'y a pas de dépendance forte avec l'objet réel.

  3. Améliorer plusieurs fois (comme mentionné dans la question) : Proxy : nous pourrions utiliser le proxy pour envelopper l'objet réel une fois et non plusieurs fois. Décorateur : Un décorateur peut envelopper l'objet réel plusieurs fois ou peut envelopper l'objet qui est déjà enveloppé par un décorateur (qui peut être un décorateur différent ou le même décorateur). Par exemple, pour un système de commande, vous pouvez faire une remise avec des décorateurs. PercentageDiscountDecorator permet de réduire de 50% le montant de la commande, et DeductionAmountDiscountDecorator permet de déduire directement 5$ si le montant est supérieur à 10$(). Donc, 1). Quand vous voulez couper 50% et déduire 5$, vous pouvez faire : new DeductionAmountDiscountDecorator(new PercentageDiscountDecorator(delegatee)) 2). Lorsque vous voulez déduire 10$, vous pouvez faire : new DeductionAmountDiscountDecorator(new DeductionAmountDiscountDecorator(delegatee)).

La réponse à la question n'a rien à voir avec la différence entre Proxy et Decorator. Pourquoi ?

  1. Les modèles de conception ne sont que des modèles permettant aux personnes qui ne maîtrisent pas les compétences OO d'utiliser des solutions OO. Si vous êtes familier avec l'OO, vous n'avez pas besoin de savoir combien de design patterns il y a (avant l'invention des design patterns, avec le même prolbem, des personnes compétentes pouvaient trouver la même solution).
  2. Aucune feuille n'est exactement la même, de même que les problèmes que vous rencontrez. Les gens trouveront toujours que leurs problèmes sont différents des problèmes donnés par les modèles de conception.

Si votre problème spécifique est vraiment différent des deux problèmes sur lesquels travaillent Proxy et Decorator, et qu'il nécessite vraiment une agrégation, pourquoi ne pas l'utiliser ? Je pense que l'application de l'OO à votre problème est beaucoup plus importante que le fait de l'étiqueter comme Proxy ou Decorator.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X