2010-08-13 4 views
1

J'ai actuellement une implémentation de cache (en utilisant des tableaux) pour les calculs lourds effectués pendant la simulation. La structure du cache est la suivante:Comment refactoriser/étendre le modèle suivant

alt text

La façon dont cela fonctionne:

CalculationsAbstract calculationsCache = new CalculationsCache(); 

// Constructor of CalculationsCache 
public CalculationsCache() 
{ 
    this.Proxy = new Calculations(); 
    Proxy.Proxy = this; 
} 

calculationsCache.CalculateValue1();  
// Checks "Value1" array for existing value, if not, the actual computation is called 
// via Proxy object, value retrieved is cached in array then returned to user. 

Maintenant, je suis en train d'ajouter de nouveaux calculs spécifiques à un certain scénario, et wouldn Il ne convient pas de les placer dans CalculationsAbstract, Calculations et CalculationsCache, mais ScenarioA utiliserait toujours les calculs existants dans les anciennes classes. J'essaie d'ajouter les nouveaux calculs et leurs tableaux dans de nouvelles classes nommées ScenarioACalculations et ScenarioACalculationsCache, tout comme cela a été fait pour Value1, Value2, ... etc, mais je suis confus quant à la façon dont ces nouvelles classes seraient s'intégrer dans le modèle existant.

C'est ce que j'ai essayé de le faire:

internal interface IScenarioACalculations 
{ 
    float GetScenarioAValue5(); 
} 

ScenarioACalculations : Calculations, IScenarioACalculations 
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations 

Étant donné que tout au long de mon projet, je tiens seulement une référence au type CalculationsAbstract (comme dans l'exemple de code ci-dessus), et je ne peux pas jeter mon IScenarioACalculations object à CalculationsAbstract, quelle serait la meilleure façon d'ajouter des calculs ScenarioA et éventuellement ScenarioB ... etc dans le futur?

Désolé de faire cela longtemps.

Merci.

Répondre

3

Fondamentalement, vous avez un décorateur dans CalculationsCache. Étant donné que l'écriture d'un tel calque autour de votre objet Calculs pose un problème de maintenance, vous devez placer la mise en cache dans l'objet Calculs lui-même.

Essayez ceci:

Scenario 
{ 
    IDictionary<int, float> cache = new Dictionary<int, float>; 

    void Reset() 
    { 
    cache.Clear(); 
    } 
} 

PhysicsScenario : Scenario 
{ 
    const int AccelerationType = 1; // or string, or whatever. Just needs to be a key into the cache. 
    float[] CalculateAcceleration() 
    { 
    if (cache.Keys.Contains(AccelerationType)) 
    { 
     return cache[AccelerationType]; 
    } 
    else 
    { 
     float[] values = ActualCalculateAcceleration(); 
     cache.Add(AccelerationType, values); 
     return values; 
    } 
    } 
    float[] CalculateVelocity() {...} 
    // More heavy calculations 
} 

ChemistryScenario : Scenario 
{ 
    float[] CalculateVolume() {...} 
    float[] CalculateCalorificValue() {...} 
    // More heavy calculations 
} 

Vous ne parlez pas de votre gestion du cycle de vie de votre objet de cache. Si c'est exactement la même chose que votre objet Calculs, vous pouvez probablement l'utiliser.

S'il se passe quelque chose d'autre, vous pouvez transmettre le dictionnaire Dictionary depuis le conteneur en utilisant les principes Inversion of Control.

1

Il semble que la façon dont vous avez décrit ce modèle indique que les deux méthodes de CalculationsAbstract sont toujours remplacées. Pourquoi ne pas faire une interface ICalculationsAbstract et ont

internal interface IScenarioACalculations : ICalculationsAbstract 
{ 
    float GetScenarioAValue5(); 
} 

Vous pouvez toujours avoir:

ScenarioACalculations : Calculations, IScenarioACalculations 
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations 

Mais maintenant votre IScenarioACalculations peut être jeté dans une ICalculationsAbstract

1

Je vois encore la question de toujours avoir tout modifier pour ajouter plus de calculs. Peut-être votre ICalculations devrait avoir une seule méthode Calculate() qui a pris un IScenario. Le IScenario fournirait une méthode pour exécuter le scénario et le retour, une méthode pour vérifier l'égalité, obtenir le hashcode, et peut-être une méthode à cloner.L'idée est que chaque nouveau calcul que vous souhaitez ajouter est une classe distincte. Ils pourraient chacun être créés et transmis à un ICalculations qui pourrait alors décider quoi en faire. Très probablement l'implémentation vérifie le hashcode IScenario's par rapport à un cache de quelque sorte, si trouvé, retourne la valeur, sinon appelle le IScenario's Execute() stocke la valeur avec le hashcode IScenario's pour la mise en cache puis retourne. Donc, ce serait essentiellement Command Pattern. Ce que je vois vous gagnez, c'est que la mise en œuvre des ICalculations ne devra pas changer très souvent (en supposant que je comprenne ce que vous faites) et il y a un point d'extension facile. Vous pourriez également ajouter plus tard des idées de contexte d'exécution et faire des composites de scénarios pour permettre des choses comme des calculs distribués qui sont agrégés mais pour l'utilisateur des classes/interfaces très peu devraient changer alors que vous pouvez éliminer ces complexités dans le les classes qui en ont besoin. Les inconvénients que je vois est que vous devez savoir pour remplacer la méthode GetHashCode() et Equals() pour chaque scénario. Peut-être capable de le faire de manière générique (ceci inclut le Clone()) en utilisant la réflexion si vous savez que vous vous souciez seulement des propriétés publiques qui représenteront toujours les paramètres pour le calcul par exemple. Aussi, je sais que ReSharper peut générer ces méthodes pour vous.

Cela serait trop avancé si vous étiez seulement préoccupé par le fait que 3 calculs ne changeaient pas, mais si vous deviez ajouter/maintenir plusieurs à différents moments de complexités différentes, alors l'approche a probablement du mérite.

Juste un code de base comme par exemple:

interface ICalculations { 
    double Calculate(IScenario scenario); 
} 

interface IScenario{ 
    double Execute(); 
    IScenario Clone(); 
} 

public class CalculationsCacher: ICalculations { 
    IDictionary<IScenario, double> _cache; 

    public CalculationsCacher(IDictionary<IScenario, double> existingCache = new Dictionary<IScenario, double>()){ 
     //c#4 has optional parameters 
     _cache = existingCache 
    } 

    public double Calculate(IScenario scenario){ 
     if(_cache.ContainsKey[scenario]) return _cache[scenario]; 

     _cache[scenario.Clone()] = scenario.Execute(); 
     return _cache[scenario]; 
    } 
} 

class AccelerationScenario: IScenario{ 
    // properties 
    public AccelerationScenario(double distance, TimeSpan time){ 
     // set things 
    } 

    public double Execute(){ 
     //calculate 
    } 

    public IScenario Clone(){ 
     //use contructor to build a new one 
    } 

    public override int GetHashCode(){ 
     //generate the hashcode 
    } 

    public override bool Equals(object obj){ 
     //you should also override this when you override GetHashCode() 
    } 
} 

//somewhere in your app 
var scenario = new AccerlationScenario(/* values */); 
return calculations.Calculate(scenario); 

I a ajouté la méthode clone dans le cas où les propriétés sont inscriptibles vous ne voulez pas modifier les paramètres d'un scénario mis en cache et dévalider la valeur stockée.

Questions connexes