Décorateur (patron de conception)

Un article de Wikipédia, l'encyclopédie libre.
Sauter à la navigation Sauter à la recherche

En génie logiciel, un décorateur est le nom d'une des structures de patron de conception.

Un décorateur permet d'attacher dynamiquement de nouvelles responsabilités à un objet. Les décorateurs offrent une alternative assez souple à l'héritage pour composer de nouvelles fonctionnalités.

Aperçu[modifier | modifier le code]

Le pattern Décorateur est l'un des vingt-trois patterns GOF. Il résout les problématiques suivantes :

  • L'ajout (et la suppression) des responsabilités à un objet dynamiquement au moment de l'exécution.
  • Il constitue une alternative aux sous-classes pour une surcharge flexible des fonctionnalités.

Quand on utilise l'héritage, les différentes sous-classes étendent une classe mère en différentes manières. Mais une extension est attachée à la classe au moment de la compilation, et ne peut pas changer à l'exécution.

Exemple en Java[modifier | modifier le code]

// ______________________________________________________________________
// Déclarations
public interface IVehicule
{
    public String mNom();
    public String mMarque();
	public int mPrix();
	public int mPoids();
}

public abstract class Voiture implements IVehicule
{
	private String aNom; 
	private String aMarque;
    protected void mNom(String pNom)
    {
        this.aNom = pNom;
    }
    public String mNom()
    {
        return this.aNom;
    }
    protected void mMarque(String pMarque)
    {
        this.aMarque = pMarque;
    }
    public String mMarque()
    {
        return this.aMarque;
    }
}

public class DS extends Voiture
{
	public DS() 
    {
        this.mNom("DS");
        this.mMarque("Citroën");
	}	
	public int mPrix() 
    {
        return 30000;
    }	
	public int mPoids() 
    {
        return 1500;
    }	
}

// ______________________________________________________________________
// Décorateurs
public abstract class VoitureAvecOption extends Voiture
{
	private IVehicule aVoiture;
    protected void mVehicule(IVehicule pVehicule)
    {
        this.aVehicule = pVehicule;
    }
    public IVehicule mVehicule()
    {
        return this.aVehicule();
    }
}

class VoitureAvecToitOuvrant extends VoitureAvecOption
{	
	public int mPrix() 
    {
        return this.mVehicule().getPrix() + 10000;
    }
	public int mPoids() 
    {
        return this.mVehicule().getPoids() + 15;
    }	
}

//On garde le nom du pattern Decorator pour savoir qu'on wrap un objet 
class DSAvecToitOuvrantDecorator extends VoitureAvecToitOuvrant
{
	public DSAvecToitOuvrantDecorator(DS pDS) 
    {
		this.mVehicule(pDS);
	}
}

public class Main 
{
	// ______________________________________________________________________
	// Implémentation
	public static void main(String[] args) 
    {
		IVehicule vDS = new DS();
		IVehicule vDSOption = new DSAvecToitOuvrantDecorator(vDS);
		System.out.println(vDSOption.getPoids() + " - " + vDSOption.getPrix());
	}
}

Exemple en C#[modifier | modifier le code]

Ici l'héritage est utilisé.

//______________________________________________________________________
// Déclarations

abstract class Voiture {
    public abstract double Prix { get; }
}
class  AstonMartin : Voiture {
    public override double Prix { get { return 999.99; } }
}

//______________________________________________________________________
// Décorateurs

class Option : Voiture {
    protected Voiture _originale;
    protected double _tarifOption;
    public Option(Voiture originale, double tarif) { 
        _originale = originale;
        _tarifOption = tarif; 
    }
    public override double Prix {
        get { return _originale.Prix + _tarifOption; }
    }
}

class VoitureAvecClimatisation : Option {
    public VoitureAvecClimatisation (Voiture originale) : base(originale, 1.0) { }
}
class VoitureAvecParachute : Option {
    public VoitureAvecParachute (Voiture originale) : base(originale, 10.0) { }
}
class VoitureAmphibie : Option {
    public VoitureAmphibie (Voiture originale) : base(originale, 100.0) { }
}

//______________________________________________________________________
// Implémentation

class Program {
    static void Main() {
        Voiture astonMartin= new AstonMartin();
        astonMartin = new VoitureAvecClimatisation(astonMartin);
        astonMartin = new VoitureAvecParachute(astonMartin);
        astonMartin = new VoitureAmphibie(astonMartin);

        Console.WriteLine(astonMartin.Prix); // affiche 1110.99
    }
}

Exemple en C++[modifier | modifier le code]

# include <iostream>
# include <memory>

class IVoiture
{
public:
  virtual double prix() = 0;
};

class Aston_martin : public IVoiture
{
public:
  double prix() override { return 999.99l; }
};

class Option_voiture : public IVoiture
{
public:
  Option_voiture(std::unique_ptr<IVoiture> voiture, double prix_option)
    : voiture_(std::move(voiture))
    , prix_option_(prix_option)
  {}
  double prix() override { return voiture_->prix() + prix_option_; }
protected:
  std::unique_ptr<IVoiture> voiture_;
  double prix_option_;
};

class Option_clim : public Option_voiture
{
public:
  Option_clim(std::unique_ptr<IVoiture> voiture) : Option_voiture(std::move(voiture), 1.0) {}
};

class Option_parachute : public Option_voiture
{
public:
  Option_parachute(std::unique_ptr<IVoiture> voiture) : Option_voiture(std::move(voiture), 10.0) {}
};

class Option_amphibie : public Option_voiture
{
public:
  Option_amphibie(std::unique_ptr<IVoiture> voiture) : Option_voiture(std::move(voiture), 100.0) {}
};

int main()
{
  auto voiture = std::unique_ptr<IVoiture>(std::make_unique<Aston_martin>());
  voiture = std::make_unique<Option_clim>(std::move(voiture));
  voiture = std::make_unique<Option_parachute>(std::move(voiture));
  voiture = std::make_unique<Option_amphibie>(std::move(voiture));
  std::cout << voiture->prix() << "\n"; // affiche 1110.99
  return 0;
}

Exemple en PHP[modifier | modifier le code]

<?php

// Interface pour rendre un objet affichable
interface Affichable {
	public function affiche();
}

// Classe contenant un message affichable
class Message implements Affichable {

	protected $message = '';
	
	public function __construct($message) {
		$this->message = $message;
	}
	
	public function affiche() {
		echo $this->message;
	}

}

// Une classe abstraite de décoration de message affichable
abstract class DecorateurDeMessage implements Affichable {
	
	protected $messageDecore = null;
 
	public function __construct(Affichable $messageDecore) {
		$this->messageDecore = $messageDecore;
	}
}

// Une classe pour "décorer" un message en gras
class MessageEnGras extends DecorateurDeMessage {

	public function affiche() {
		echo '<strong>';
		$this->messageDecore->affiche();
		echo '</strong>';
	}
}
 
// Une classe pour "décorer" un message en italique
class MessageEnItalique extends DecorateurDeMessage {

	public function affiche() {
		echo '<em>';
		$this->messageDecore->affiche();
		echo '</em>';
	}
}

// Création du message
$message = new Message('le message');

// On met le message en gras et en italique
$messageDecore = new MessageEnItalique( new MessageEnGras( $message ) );
// On affiche le message décoré
$messageDecore->affiche();
?>

Exemple en Delphi[modifier | modifier le code]

En effet, les langages Delphi et Free Pascal supportent les class helpers qui rendent inutile le patron de conception décorateur.

program NoMoreDecorators;

type
  TMyObject = class
    procedure WriteHello;
  end;

  TMyObjectHelper = class helper for TMyObject
    procedure WriteHello(const Name: string); overload;
  end;

procedure TMyObject.WriteHello;
begin
  writeln('Hello');
end;

procedure TMyObjectHelper.WriteHello(const Name: string);
begin
  writeln('Hello, ', Name, '!');
end;

var
  o: TMyObject;
begin
  o := TMyObject.Create;
  o.WriteHello;
  o.WriteHello('Jean');
  o.Free;
end.

source : Delphi GOF DesignPatterns (CodePlex)

Notes et références[modifier | modifier le code]

Annexes[modifier | modifier le code]

Articles connexes[modifier | modifier le code]

Sur les autres projets Wikimedia :