首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >避免在几个类中进行更改的设计建议

避免在几个类中进行更改的设计建议
EN

Stack Overflow用户
提问于 2010-04-27 15:36:27
回答 4查看 195关注 0票数 2

我正在试图弄清楚如何设计一个更优雅的小应用程序,并让它更能抵抗变化。

基本上,它是一种项目价格计算器,问题是有许多参数可以影响定价。我尽量避免在代码中为每个参数使用大量的if子句,但是我仍然在两个地方使用if子句来检查size参数的值。

我有Head First Design pattern这本书,并试图在那里找到想法,但我得到的最接近的是装饰者模式,其中有一个示例,starbuzz咖啡首先根据添加的调味品设定价格,然后在练习中通过添加大小参数(Tall,Grande,Venti)来设置价格。但这似乎没有帮助,因为添加该参数似乎仍然在许多地方增加了if-子句的复杂性(这是一个练习,他们没有进一步解释)。

我试图避免的是,如果要更改一个参数或添加一个新参数,则必须更改几个类,或者至少在尽可能少的地方更改(对于此,有一些花哨的设计原则词,我不记得了:-))。

下面是代码。基本上,它使用一个大小参数和不同的定价模型来计算具有“编写”和“分析”任务的项目的价格。稍后还会有其他参数出现,比如“产品有多新?”(新的,1-5岁,6-10岁)等。任何关于最佳设计的建议都将非常受欢迎,无论是“设计模式”还是良好的面向对象的原则,使其无法更改(例如,添加另一个大小,或更改一个大小的值,并且只需在一个地方而不是在几个if子句中更改):

代码语言:javascript
复制
public class Project
{
    private readonly int _numberOfProducts;
    protected Size _size;
    public Task Analysis { get; set; }
    public Task Writing { get; set; }

    public Project(int numberOfProducts)
    {
        _numberOfProducts = numberOfProducts;
        _size = GetSize();
        Analysis = new AnalysisTask(numberOfProducts, _size);
        Writing = new WritingTask(numberOfProducts, _size);

    }

    private Size GetSize()
    {
        if (_numberOfProducts <= 2)
            return Size.small;
        if (_numberOfProducts <= 8)
            return Size.medium;
        return Size.large;
    }
    public double GetPrice()
    {
        return Analysis.GetPrice() + Writing.GetPrice();
    }
}

public abstract class Task
{
    protected readonly int _numberOfProducts;
    protected Size _size;
    protected double _pricePerHour;
    protected Dictionary<Size, int> _hours;
    public abstract int TotalHours { get; }

    public double Price { get; set; }

    protected Task(int numberOfProducts, Size size)
    {
        _numberOfProducts = numberOfProducts;
        _size = size;
    }

    public double GetPrice()
    {
        return _pricePerHour * TotalHours;
    }
}

public class AnalysisTask : Task
{
    public AnalysisTask(int numberOfProducts, Size size)
        : base(numberOfProducts, size)
    {
        _pricePerHour = 850;
        _hours = new Dictionary<Size, int>() { { Size.small, 56 }, { Size.medium, 104 }, { Size.large, 200 } };
    }

    public override int TotalHours
    {
        get { return _hours[_size]; }
    }
}

public class WritingTask : Task
{
    public WritingTask(int numberOfProducts, Size size)
        : base(numberOfProducts, size)
    {
        _pricePerHour = 650;
        _hours = new Dictionary<Size, int>() { { Size.small, 125 }, { Size.medium, 100 }, { Size.large, 60 } };
    }

    public override int TotalHours
    {
        get
        {
            if (_size == Size.small)
                return _hours[_size] * _numberOfProducts;
            if (_size == Size.medium)
                return (_hours[Size.small] * 2) + (_hours[Size.medium] * (_numberOfProducts - 2));
            return (_hours[Size.small] * 2) + (_hours[Size.medium] * (8 - 2)) + (_hours[Size.large] * (_numberOfProducts - 8));
        }
    }
}

public enum Size
{
    small, medium, large
}

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 comboBoxNumberOfProducts_SelectedIndexChanged(object sender, EventArgs e)
    {
        Project project = new Project((int)comboBoxNumberOfProducts.SelectedItem);
        labelPrice.Text = project.GetPrice().ToString();
        labelWriterHours.Text = project.Writing.TotalHours.ToString();
        labelAnalysisHours.Text = project.Analysis.TotalHours.ToString();
    }
}

最后是一个简单的当前调用代码,该代码位于组合框的change事件中,用于设置size...(顺便说一句,我不喜欢我必须在最后使用几个点才能到达TotalHours的事实,据我所知,这也违反了“最低知识原则”或“德米特定律”,所以对此提出意见也很感谢,但这不是问题的重点)

致以敬意,

安德斯

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2010-04-27 16:00:36

首先,在我看来,你应该重新考虑你的设计。项目看起来不是这样的,就我在您的代码中所看到的,您没有办法向项目中添加更多的任务。也可以考虑将项目和计算奖金的方式分开。如果你有不同的计算方法怎么办?这也与责任有关,很快你的项目可能会增长,很难区分你计算价格的方式和项目结构。通常情况下,避免"if“是使用多态性完成的-也许你应该根据它们的参数拥有不同的项目类型。这可以使用Factory方法来实现,它将接受参数,做一次"if"s,然后创建一些Project子类型,它将知道如何正确地计算其奖励。如果您将项目和计算分开,那么可以考虑使用策略模式来计算奖金。对德米特定律的关注在这里是足够的,因为你暴露了任务。尝试使用返回总价并委托的方法。原因是,方法所在类(项目或计算策略)可以决定如何计算它,它也可以从其他任务中获取信息。如果你计划添加更多的任务,你将不得不调整方法,可能会使用一个带有字符串或枚举参数的方法来选择一个具体的任务来计算奖金。顺便说一句。你为什么这么多地使用下划线?

票数 3
EN

Stack Overflow用户

发布于 2010-04-29 04:48:57

如果您有这种基于类属性的if...else语句,请尝试使用策略模式对其进行重构。你可以试试"Refactor to Patterns“这本书,这是一本关于重构的好书。

票数 1
EN

Stack Overflow用户

发布于 2010-04-27 16:33:03

所以你设计的应用程序有我想说的设计原则中的一个主要差距:

它假设一个单一使用数据集。

我的意思是,它假设只有两个可能的任务,每个任务都有一个硬编码的价格(这在商业世界中根本不存在),并且每个任务计算出的“小时”相对于一组恒定的大小是确定的。我的建议是,通过使用数据库来存储新的可能任务/属性/价格/每小时比率/大小,或者通过一些其他存储手段,并编写用于管理它的配置表单,使几乎所有这些都是可配置的。

这几乎可以立即消除您所暗示的设计问题,因为您删除了所有硬编码的域上下文,而是设置了一个配置先例,如果有人不喜欢您的配置方法或希望以其他方式使用此配置方法,则可以通过API公开此先例。

编辑:我想在下面发表评论,但空间用完了:

扩展xml的深度,以便有意义地表示更大的数据结构(WritingTask和AnalysisTask)及其组件部分(属性和方法)。这些方法通常可以由一组规则来定义。您可以对属性和规则进行标记,以便可以独立地与其交互。示例:

代码语言:javascript
复制
<task name="WritingTask">
<property name="numberofproducts" type="int"/>
<property name="Size" type="size">
    <property name="Price" type="decimal">
    <param name="priceperhour" value="650">
    </property>
<property name="hours" type="Dictionary">
    <param name="Size.small" value="125"/> 
    <param name="Size.medium" value="100"/>     
        <param name="Size.large" value="60"/>   
    </property>     
    <method name="TotalHours">
        <rule condition="_size == Size.Small">
    <return value="_hours[_size] * _numberofproducts"/>
        </rule>
        <rule condition="_size == Size.medium">
        <return value="(_hours[Size.small] * 2) + (_hours[Size.medium] * _numberOfProducts - 2))"/>
    </rule>
    <return value="(_hours[Size.small] * 2) + (_hours[Size.medium] * (8 - 2)) + (_hours[Size.large] * (_numberOfProducts - 8))"/> 
    </method> 
</task>

无论如何,现在对我来说已经太晚了,我不能再继续尝试了,但是我会在明天和你一起跟进。通过设置结果属性并将配置中的方法与规则相关联,您可以将if的协调留给您的数据集(它应该知道得最清楚),并调整您的代码以准确解释此数据。它保持足够的灵活性来处理增长(通过用于数据集创建的sudo语言),但在内部保持不变,不需要改进更通用的操作。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2719466

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档