web-dev-qa-db-fra.com

Comprendre le modèle de conception de décorateur en C #

Je viens juste de commencer à apprendre le motif de conception de décorateur. Malheureusement, j’ai dû passer par différentes références pour mieux comprendre le motif de décorateur, ce qui m’a mené dans une grande confusion. donc, autant que je sache, je pense que c'est un motif de décorateur

interface IComponent
    {
        void Operation();
    }
    class Component : IComponent
    {
        public void Operation()
        {
            Console.WriteLine("I am walking ");
        }
    }
    class DecoratorA : IComponent
    {
        IComponent component;
        public DecoratorA(IComponent c)
        {
            component = c;
        }
        public void Operation()
        {
            component.Operation();
            Console.WriteLine("in the rain");
        }
    }
    class DecoratorB : IComponent
    {
        IComponent component;
        public DecoratorB(IComponent c)
        {
            component = c;
        }
        public void Operation()
        {
            component.Operation();
            Console.WriteLine("with an umbrella");
        }
    }
    class Client
    {
        static void Main()
        {
            IComponent component = new Component();
            component.Operation();

            DecoratorA decoratorA = new DecoratorA(new Component());
            component.Operation();

            DecoratorB decoratorB = new DecoratorB(new Component());
            component.Operation();

            Console.Read();
        }
    }

mais le code ci-dessous peut-il aussi être un motif de décorateur?

class Photo
{
    public void Draw()
    {
        Console.WriteLine("draw a photo");
    }
}
class BorderedPhoto : Photo
{
    public void drawBorder()
    {
        Console.WriteLine("draw a border photo");
    }
}
class FramePhoto : BorderedPhoto
{
    public void frame()
    {
        Console.WriteLine("frame the photo");
    }
}
class Client
{
    static void Main()
    {
        Photo p = new Photo();
        p.Draw();

        BorderedPhoto b = new BorderedPhoto();
        b.Draw();
        b.drawBorder();

        FramePhoto f = new FramePhoto();
        f.Draw();
        f.drawBorder();
        f.frame();
    }
}

Ma compréhension

à partir du deuxième exemple que j'ai donné, nous pouvons appeler les trois méthodes, mais à partir du premier exemple, je ne pourrai pas accéder à ces trois méthodes en créant un seul objet.

10
Lijin Durairaj

Ce devrait être un commentaire, mais j'ai trop de mots.

Par exemple, vous avez un objet et une interface, comme Repository : IRepository.

public interface IRepository
{
    void SaveStuff();
}

public class Repository : IRepository
{
    public void SaveStuff()
    {
        // save stuff   
    }
}

et client, qui n'a probablement pas été écrit par vous

class RepoClient
{
    public void DoSomethig(IRepository repo)
    {
        //...
        repo.SaveStuff();
    }
}

Et une fois que vous avez décidé que TOUS les appels au référentiel doivent être consignés. Mais vous avez un problème - Repository - classe de la bibliothèque externe et vous ne voulez pas changer ce code. Vous devez donc étendre le comportement du référentiel que vous utilisez. Vous écrivez RepositoryLogDecorator : IRepository, et à l'intérieur de chaque méthode, enregistrez, comme 

public class RepositoryLogDecorator  : IRepository
{
    public IRepository _inner;

    public RepositoryLogDecorator(IRepository inner)
    {
        _inner = inner;
    }

    public void SaveStuff()
    {
        // log enter to method
        try
        {
            _inner.SaveStuff();
        }
        catch(Exception ex)
        {
            // log exception
        }       
        // log exit to method
    }
}

Donc, avant de pouvoir utiliser le client comme 

var client = new RepoClient();
client.DoSomethig(new Repository());

mais maintenant vous pouvez utiliser

var client = new RepoClient();
client.DoSomethig(new RepositoryLogDecorator(new Repository()));

Notez que ceci est un exemple très simple. Dans les projets réels, où l'objet créé primaire avec le conteneur DI, vous pouvez utiliser Decorator en modifiant certaines configurations. 

Donc, quoi pour décorator est utilisé: étendre la fonctionnalité d'un objet sans changer d'objet ou de client.

Autre avantage du décorateur: vos décorateurs ne dépendent pas de la mise en œuvre de Repository. Ne dépend que de l'interface IRepository. Pourquoi est-ce avantage? Si vous décidez de rédiger votre propre implémentation 

public class MyAwesomeRepository : IRepository
{
    public void SaveStuff()
    {
        // save stuff, but AWESOME!
    }
}

vous pouvez automatiquement décorer avec décorateur, ce qui existe déjà

var client = new RepoClient();
client.DoSomethig(new RepositoryLogDecorator(new MyAwesomeRepository()));

Vous voulez voir un exemple d'un logiciel réel? (juste comme exemple, le code est moche, je sais) => allez ici

Cool! Aimer! :RÉ

28
tym32167

Il y a ceci La série PatternCraft sur Youtube qui explique Design Patterns avec Starcraft, vous devriez regardez la vidéo sur les décorateurs ici .

Dans la vidéo ci-dessus, l'auteur donne un exemple avec Marine et WeaponUpgrade.

Dans le jeu, vous aurez une Marine et vous pourrez ensuite améliorer son arme:

marine = new WeaponUpgrade(marine);

Notez que vous avez toujours une marine là-bas, ce n'est pas une nouvelle unité, c'est la même unité avec des choses qui modifient ses attributs.

public class MarineWeaponUpgrade : IMarine
{
    private IMarine marine;

    public MarineWeaponUpgrade(IMarine marine)
    {
        this.marine = marine;
    }

    public int Damage
    {
        get { return this.marine.Damage + 1; } // here
        set { this.marine.Damage = value; }
    }
}

Pour cela, vous créez une classe qui implémente la même interface que votre unité et accédez aux propriétés de votre unité pour modifier les valeurs.

Il y a un Kata sur CodeWars vous mettant au défi de compléter les décorateurs d’armes et d’armures pour une marine.

9
BrunoLM

Per page GOF Motif de décorateur:

Attachez des responsabilités supplémentaires à un objet de manière dynamique. Les décorateurs offrent une alternative flexible au sous-classement pour étendre les fonctionnalités.

Dans votre deuxième exemple, vous utilisez l'héritage pour étendre le comportement d'une classe. Je pense que ce n'est techniquement pas un modèle de conception de décorateur.

4
brakeroo

Le modèle de décorateur vous permet d’ajouter un comportement spécifique à un objet individuel d’un type donné _ sans affecter d’autres instances du même type.

Dans votre deuxième exemple, qui correspond à l'héritage normal, les instances toutes de la classe héritent du comportement modifié.

3
InBetween