2009-05-31 17 views
3

Comment refactoriser cette conception? Le problème est que ces services sont facturés à des débits variés, selon qu'ils sont extensibles ou en fonction de leur type.
Un pourcentage du montant facturé est facturé si le service est prolongé. Les frais de service sont ChargeableAmount * Taux saisonnier, qui diffère pour chaque service.Comment refactoriser cette conception

Je ne veux pas stocker les tarifs dans la classe de service ou l'une des classes dérivées. Serait-ce une bonne idée de créer un fournisseur SeasonalRate qui a des méthodes pour chaque ServiceType qui retourne le SeasonalRate?
Le problème est que les taux saisonniers peuvent changer et changent souvent et nous hésitons à apporter des changements aux classes de service. Nous aimerions également avoir des dépendances maintenables sur les classes de service dans nos classes SeasonalRates.
Est-ce une bonne idée d'implémenter une classe SeasonalRates avec des surcharges pour chaque classe ServiceClass (une sorte de visiteur)? Serait-ce un problème que toutes les classes SeasonalRates devront implémenter des méthodes pour tous les ServiceTypes?
Quelle interface la classe SeasonalRate doit-elle implémenter car il y aura également d'autres classes qui calculeront des taux tels que DiscountRate, ZipCodeRate, etc.?
Si plus de classes ServiceType sont ajoutées, sera-t-il difficile de maintenir les modifications à tous les visiteurs?

Modifier: Tous les tarifs varient en fonction du type de service. Par exemple, SeasonalRates jour et nuit services sont différents

Quels seraient les avantages/inconvénients de l'approche proposée par Jeff Meatball Yang plus en utilisant le modèle de visiteur standard comme suit:

interface IVisitable 
{ 
    Accept(IVisitor visitor); 
} 
interface IVisitor 
{ 
    Visit(IVisitable visitable); 
} 
interface IRate 
{ 
    Rate { get; } 
} 

class Service : IVisitable 
{ 
    public virtual Accept(IVisitor visitor); 
} 
class Visitor : IVisitor 
{ 
    public virtual Visit(IVisitable visitable) { } 
    public virtual Visit(DayService visitable) { } 
    public virtual Visit(NightService visitable) { } 
} 
class RateVisitor : Visitor, IRateVisitor 
{ 
    decimal? _Rate; 

    override Visit(IVisitable visitable) { }; 
    override Visit(DayService visitable) { // Logic Goes here, sets the _Rate Variable }; 
    // Overrides for other services 

    public virtual Rate 
    { 
     return this._Rate; 
    } 
} 

Répondre

2

Il semble que vous cherchez le pattern Strategy: plusieurs façons de calculer quelque chose? utilisez simplement une interface commune et implémentez des comportements différents dans des objets concrets.

Qu'en est-:

class Service : IChargeable, IExtendable 
{ 

} 

interface IChargeable { 
    decimal? ChargeableAmount { get; } 
} 

interface IExtendable { 
    bool IsExtendable { get; } 
} 

class RatedService: Service { 
    RateStrategy strategy; 
} 

interface RateStrategy { 
    decimal SeasonalRate { get; } 
} 

class DiscountRate: RateStrategy {} 
class ZipCodeRate: RateStrategy {} 

class DayService : RatedService { } 
class NightService : RatedService { } 
class TwentFourService : RatedService { } 
+0

Salut. Merci pour les ans. Le problème est que toutes les RateStrategies ne sont pas identiques. ZipCodeRate par exemple ne retournera jamais une valeur nulle. De même, il y a d'autres opérations à ajouter, par exemple obtenir une vignette de produit, etc. Cela nécessiterait beaucoup de différents types de base de stratégie et des changements fréquents aux objets Service. –

+0

Vous voulez dire des changements aux objets hérités de RatedService? Chaque fois que vous souhaitez modifier la stratégie utilisée par un type de service, vous devez modifier quelques lignes dans la classe héritée. Aka si vous ne payez pas DayService, vous devrez changer quelque chose dans cette classe. Ce que je propose ici, est de changer: DayService() {super (nouveau DefaultStrategy();} à: DayService() {super (nouveau Discount (0,8));} Il ressemble à un très petit montant des changements ... – NicDumZ

+1

+1: Je fais quelque chose comme ça dans notre application, mais pour les calculs de taxes: J'ai une méthode de stratégie dif selon la province dans laquelle l'application fonctionne. –

0

Ceci est un problème intéressant. Je voudrais réfléchir sur l'objectif de ce design, afin que vous puissiez me corriger si je me trompe. D'après votre description, il semble qu'un service peut avoir plusieurs tarifs. Une instance de service est lié à l'un des cas suivants (ou plus?):

SeasonalRate, DiscountRate, ZipCodeRate, etc.

Je crois aussi qu'il ya une telle chose comme un objet Visiteur et elle se rapporte à un ou plusieurs services:

DayService, NightService, TwentyFourService, etc.

Je crois que la tâche ici est de facturer correctement un visiteur, selon les propriétés du service en cours d'exécution, et les tarifs applicables?

La visualisation des relations devrait vous aider à comprendre les interfaces nécessaires. Il semble que tous les tarifs doivent implémenter une interface que tous les services peuvent appeler.

public interface IRate { 
    decimal GetRate(Service s); 
} 

Ceci permet aux tarifs de décider, en fonction du service, quelle devrait être la valeur de retour. Maintenant, il serait important que tous les services implémentent les interfaces (IChargeable, IEextendable, etc.) afin que toutes les données nécessaires pour prendre une décision soient disponibles pour chaque objet Rate.

Voici un exemple de ce que je pense:

public interface IThumbnail { 
    string ThumbnailFile { get; } 
    int ThumbnailSize { get; } 
    Image GetThumbnail(); 
} 
public interface IThumbnailProvider { 
    Image GetThumbnail(Service s); 
} 

public class Thumbnail : IThumbnailProvider { 
    public Image GetThumbnail(Service s) { 
     if(s.ThumbnailFile != null) { 
      // get the image 
     } 
    } 
} 

public interface ISeasonal { 
    SeasonType Season { get; } 
} 

public class SeasonalRate : IRate { 
    public decimal GetRate(Service s) { 
    if(s.Season != SeasonType.None) { 
     // return seasonal rate based on s.Season 
    } else { 
     // throw an exception or do something else 
    } 
} 

public abstract class Service : IChargeable, IExtendable, ISeasonal, IThumbnail { 
    // default implementation for ISeasonal 
    protected SeasonType _season = SeasonType.None; 
    public SeasonType Season { get { return _season; } } 


    // default implementation for IThumbnail 
    protected string _thumbFile = null; 
    protected int _thumbSize = 32; 
    public string ThumbnailFile { get { return _thumbFile; } } 
    public int ThumbnailSize { get { return _thumbnailSize; } } 
    public Image GetThumbnail() { 
    return null; // 
    } 
} 

public class DayService : Service { 
    private IRate _confirmedRate; 
    private IThumbnailProvider _fileServer; 
    public decimal GetRate() { return confirmedRate.GetRate(this); } 
    public Image GetThumbnail() { return fileServer.GetThumbnail(); } 

    // constructor 
    public DayService(IThumbnailProvider fileServer, IRate confirmedRate) { 
     this._season = SeasonType.Day; 
     this._filename = "sunny.jpg"; 
     this._fileServer = fileServer; 
     this._confirmedRate = confirmedRate; 
    } 
} 

Cela permet à vos objets de taux changent en fonction de vos besoins, mais réduit les changements nécessaires à chaque service, car devrait être assez bien défini vos variables d'affaires et relativement immuable dans la classe de base Service. Par exemple, votre service NightService peut ne pas avoir de vignette et n'a donc pas besoin d'implémenter explicitement la fonction GetThumbnail. Il hérite simplement de l'implémentation par défaut sur Service.

+0

Salut, le taux est juste un exemple. Il existe également d'autres propriétés telles que la description du service, la vignette, etc. Si les propriétés nw sont introduites, la classe de service devra changer, c'est pourquoi nous sommes allés initialement avec le visiteur –

+0

De plus, les tarifs saisonniers varient en fonction du service –

+0

ne pas avoir de mal à ajouter et implémenter de nouvelles propriétés (interfaces) sur la classe Service, puisque c'est vous r classe de base (abstraite) dont dérivent tous les autres services. Chaque propriété implémentera une interface qui prend le service en paramètre. Voir mes modifications. –

Questions connexes