J'ai déjà essayé de poser une variante de cette question. J'ai eu quelques réponses utiles, mais toujours rien qui me semblait tout à fait juste. Il me semble que cela ne devrait pas être si difficile à cerner, mais je ne suis pas capable de trouver une solution simple et élégante. (Voici mon précédent article, mais s'il vous plaît, essayez d'abord de considérer le problème comme étant le code de procédure afin de ne pas être influencé par l'explication précédente qui semblait conduire à des solutions très compliquées: Design pattern for cost calculator app?)Remplacer conditionnel avec refactorisation de polymorphisme ou similaire?
Fondamentalement, le problème est pour créer une calculatrice pour les heures nécessaires pour les projets qui peuvent contenir un certain nombre de services. Dans ce cas "écriture" et "analyse". Les heures sont calculées différemment pour les différents services: l'écriture est calculée en multipliant un taux horaire «par produit» par le nombre de produits, et plus les produits sont inclus dans le projet, plus le taux horaire est bas, mais le nombre total de les heures sont accumulées progressivement (c.-à-d. pour un projet de taille moyenne, vous prenez à la fois la petite échelle de prix, puis vous ajoutez le prix moyen de gamme au nombre de produits réels). Alors que pour l'analyse, c'est beaucoup plus simple, c'est juste un taux global pour chaque gamme de taille. Comment pourriez-vous refactoriser cela en une version élégante et de préférence simple orientée objet (s'il vous plaît noter que je ne l'écrirais jamais comme cela d'une manière purement procédurale, c'est juste pour montrer le problème d'une autre manière succinctement). J'ai pensé en termes de modèles d'usine, de stratégie et de décorateur, mais je n'arrive pas à bien les faire fonctionner. (J'ai lu Head First Design Patterns il y a quelque temps, et les motifs de décorateur et d'usine décrits ont quelques similitudes avec ce problème, mais j'ai du mal à les voir comme de bonnes solutions. , mais peut-être que ça pourrait mieux fonctionner ici, je ne sais pas ... Au moins le fait que le calcul des heures s'accumule progressivement m'a fait penser au motif décorateur ... Et l'exemple de modèle d'usine du livre à la pizzeria. Il semble juste que cela crée une explosion de classes aussi ridicule, du moins dans leur exemple: j'ai déjà trouvé bon usage des modèles d'usine, mais je ne vois pas comment je pourrais l'utiliser ici sans avoir un ensemble de cours vraiment compliqués
Le but principal serait de n'avoir à changer qu'à un seul endroit (couplage lâche, etc.) si je devais ajouter un nouveau paramètre r (disons une autre taille, comme XSMALL, et/ou un autre service, comme "Administration"). Voici l'exemple de code procédural:
public class Conditional
{
private int _numberOfManuals;
private string _serviceType;
private const int SMALL = 2;
private const int MEDIUM = 8;
public int GetHours()
{
if (_numberOfManuals <= SMALL)
{
if (_serviceType == "writing")
return 30 * _numberOfManuals;
if (_serviceType == "analysis")
return 10;
}
else if (_numberOfManuals <= MEDIUM)
{
if (_serviceType == "writing")
return (SMALL * 30) + (20 * _numberOfManuals - SMALL);
if (_serviceType == "analysis")
return 20;
}
else //i.e. LARGE
{
if (_serviceType == "writing")
return (SMALL * 30) + (20 * (MEDIUM - SMALL)) + (10 * _numberOfManuals - MEDIUM);
if (_serviceType == "analysis")
return 30;
}
return 0; //Just a default fallback for this contrived example
}
}
Toutes les réponses sont appréciées! (Mais comme je l'ai dit dans mes précédents articles, j'apprécierais les exemples de code plutôt que "Essayez ce modèle", car comme je l'ai mentionné, c'est ce qui me dérange ...) J'espère que quelqu'un a une solution vraiment élégante à ce problème que je pensais en fait depuis le début serait très simple ...
============================= ===========================
NOUVEAU AJOUT:
Je vous remercie toutes les réponses à ce jour, mais je suis Je ne vois toujours pas de solution vraiment simple et flexible au problème (celui que je pensais ne pas être très complexe au début, mais qui l'est apparemment). Il se peut aussi que je n'ai pas encore bien compris chaque réponse correctement. Mais je pensais que je posterais ma tentative actuelle de l'élaborer (avec de l'aide en lisant tous les différents angles de réponses ici). S'il vous plaît dites-moi si je suis sur la bonne voie ou non. Mais au moins maintenant, il semble que ça commence à devenir plus flexible ... Je peux facilement ajouter de nouveaux paramètres sans avoir à changer dans beaucoup d'endroits (je pense!), Et la logique conditionnelle est au même endroit. J'en ai une partie en XML pour obtenir les données de base, ce qui simplifie une partie du problème, et une partie est une tentative de solution de type stratégie.
Voici le code:
public class Service
{
protected HourCalculatingStrategy _calculatingStrategy;
public int NumberOfProducts { get; set; }
public const int SMALL = 3;
public const int MEDIUM = 9;
public const int LARGE = 20;
protected string _serviceType;
protected Dictionary<string, decimal> _reuseLevels;
protected Service(int numberOfProducts)
{
NumberOfProducts = numberOfProducts;
}
public virtual decimal GetHours()
{
decimal hours = _calculatingStrategy.GetHours(NumberOfProducts, _serviceType);
return hours;
}
}
public class WritingService : Service
{
public WritingService(int numberOfProducts)
: base(numberOfProducts)
{
_calculatingStrategy = new VariableCalculatingStrategy();
_serviceType = "writing";
}
}
class AnalysisService : Service
{
public AnalysisService(int numberOfProducts)
: base(numberOfProducts)
{
_calculatingStrategy = new FixedCalculatingStrategy();
_serviceType = "analysis";
}
}
public abstract class HourCalculatingStrategy
{
public abstract int GetHours(int numberOfProducts, string serviceType);
protected int GetHourRate(string serviceType, Size size)
{
XmlDocument doc = new XmlDocument();
doc.Load("calculatorData.xml");
string result = doc.SelectSingleNode(string.Format("//*[@type='{0}']/{1}", serviceType, size)).InnerText;
return int.Parse(result);
}
protected Size GetSize(int index)
{
if (index < Service.SMALL)
return Size.small;
if (index < Service.MEDIUM)
return Size.medium;
if (index < Service.LARGE)
return Size.large;
return Size.xlarge;
}
}
public class VariableCalculatingStrategy : HourCalculatingStrategy
{
public override int GetHours(int numberOfProducts, string serviceType)
{
int hours = 0;
for (int i = 0; i < numberOfProducts; i++)
{
hours += GetHourRate(serviceType, GetSize(i + 1));
}
return hours;
}
}
public class FixedCalculatingStrategy : HourCalculatingStrategy
{
public override int GetHours(int numberOfProducts, string serviceType)
{
return GetHourRate(serviceType, GetSize(numberOfProducts));
}
}
Et une forme simple exemple qu'il appelle (je suppose que je pourrais aussi avoir une classe enveloppe de projet avec un dictionnaire contenant les objets de service, mais je n'ai pas eu à ce):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
List<int> quantities = new List<int>();
for (int i = 0; i < 100; i++)
{
quantities.Add(i);
}
comboBoxNumberOfProducts.DataSource = quantities;
}
private void CreateProject()
{
int numberOfProducts = (int)comboBoxNumberOfProducts.SelectedItem;
Service writing = new WritingService(numberOfProducts);
Service analysis = new AnalysisService(numberOfProducts);
labelWriterHours.Text = writing.GetHours().ToString();
labelAnalysisHours.Text = analysis.GetHours().ToString();
}
private void comboBoxNumberOfProducts_SelectedIndexChanged(object sender, EventArgs e)
{
CreateProject();
}
}
(je n'ai pas pu inclure le xml, car il se est automatiquement mis en forme sur cette page, mais il est fondamentalement juste un tas d'éléments avec chaque type de service, et chaque type de service contenant les tailles avec le taux horaires en tant que valeurs.)Je ne suis pas sûr si je pousse juste le problème sur le fichier xml (je devrais encore ajouter de nouveaux éléments pour chaque nouveau type de servicet, et ajouter des éléments pour toute nouvelle taille dans chaque type de service si cela a changé .) Mais peut-être qu'il est impossible de réaliser ce que j'essaie de faire et ne pas avoir à faire au moins ce type de changement. En utilisant une base de données plutôt que xml le changement serait aussi simple que l'ajout d'un champ et une rangée:
ServiceType Small Medium Large
Rédaction 125 100 60
analyse 56 104 200
(simplement formaté comme une "table" ici, bien que les colonnes ne soient pas alignées ... je ne suis pas le meilleur en conception de base de données, et peut-être que cela devrait être fait différemment, mais vous avez l'idée ...)
S'il vous plaît dites-moi ce que vous pensez!
Il est un petit changement, et ne pas vraiment répondre à toutes vos dans le cas « écriture » question de modèle plus large, mais vous pouvez,, invoquer récursive la fonction: 'getHours de retour (petit) + 20 * _numberOfManuals - SMALL); 'Cela vous aide lorsque vous voulez ajouter XSMALL, et peut-être lire plus proprement. –
+1 Je l'aime quand les gens sont passionnés par l'écriture de code propre –