如何消除Switch语句?

5
我有一个选项卡控件,其中有3个选项卡页面。在同一表单/视图上的该选项卡控件下面,我有3个图像控件。
根据选项卡的SelectedIndex,我需要更改下面3个图像的不透明度。
目前,在选项卡控件的SelectionChanged事件中,我有以下内容:
switch (Tab.SelectedIndex)
{
     case 0:
         img1.Opacity= 1;
         img2.Opacity = 0.5;
         img3.Opacity  = 0.5;
         break;
     case 1:
         img1.Opacity = 0.5;
         img2.Opacity = 1;
         img3.Opacity  = 0.5;
         break;
     case 2:
         img1.Opacity = 0.5;
         img2.Opacity = 0.5;
         img3.Opacity = 1;
         break;
}

我该如何移除这个switch语句?在这里我应该使用哪种设计模式?
(注意:保留原有的HTML标签)

为什么要移除/替换开关?有什么问题吗? - H H
它无法进行单元测试。 - NoobDeveloper
2
是的,没错。事件驱动的环境可能会成为一个问题,但这需要一个更大的修复方案。 - H H
6个回答

1

您可以使用状态设计模式,例如在这里中解释的。您将定义几个状态,然后根据条件决定在某一时刻应使用哪些状态。

示例:

abstract class State
{
    abstract vod Apply(Form context);
}

class StateOne : State
{
    override void Apply(Form context)
    {
        img1.Opacity= 1;
        img2.Opacity = 0.5;
        img3.Opacity  = 0.5;
    }
}

你还可以将其与工厂方法设计模式相结合,该模式将决定使用哪个状态。
static class StateFactory
{
    static State GetState(condition)
    {
        if(condition == something)
            return new StateOne();
        else ...
    }
}

这不会从您的代码中删除switch语句,但至少它会在合理的位置做合理的事情。
用法:
StateFactory.GetState(condition).Apply(this);

我有点明白了。但是,你能再解释一下吗?在TabControl的SelectionChanged事件中,我怎么知道我需要哪个State对象? - NoobDeveloper
1
@Nexus 我认为你实际上可以摆脱 if 语句或 switch 语句,但是你可以将它们放在一个不经常看到的地方,只有当你需要添加新状态时才会看到它们。我已经为你更新了我的代码,并加入了工厂方法。 - Ondrej Janacek
+1,但我会使用“Dictionary<int,State>”来保存和检索状态(例如,“_states [tab.SelectedIndex] .Apply(this)”)。 - user1228
@Will 是的,这是可能的。对于像这样简单的情况使用 switch 或 if 语句是可以的。当我不想多次创建状态时,我总是使用字典。我只需创建一次并将其存储在字典中。 - Ondrej Janacek

1
提取和注入。将视图切换逻辑(switch)提取到外部类/方法中,并将其注入到您的视图中:
public void HighlightImages(int selection, params Image[] images)
{
    switch (selection)
    {
        case 0:
            images[0].Opacity= 1;
            images[1].Opacity = 0.5;
            images[2].Opacity  = 0.5;
            break;
        case 1:
            images[0].Opacity = 0.5;
            images[1].Opacity = 1;
            images[2].Opacity  = 0.5;
            break;
        case 2:
            images[0].Opacity = 0.5;
            images[1].Opacity = 0.5;
            images[2].Opacity = 1;
            break;
    }
}

在选择更改处理程序中,您只需将处理委托给注入的依赖项:
private void SelectedIndexChanged(object sender, EventArgs e)
{
    this.highlighter.HighlightImages(Tab.SelectedIndex, img1, img2, img3);
}

这样,您就可以轻松测试透明度变化的逻辑,而无需实例化完整的视图控件。

1

我认为您可以使用触发器在xaml中处理此问题。

另外,如果您希望进行单元测试,应该使用MVVM模式,在其中将在ViewModel中定义SelectedIndex,Opacities属性并将它们绑定到xaml上。


编辑:这正是我所做的。但是故意没有在我的问题中发布它。我的问题仍然是一样的。那么我该如何对我的虚拟机进行单元测试呢? - NoobDeveloper
2
如果您正在使用MVVM,则可以为ViewModel的属性编写测试用例,以测试当更改selectedindex属性时图像的不透明度是否会更改。 - Nitin

0
如果你有一堆需要传递的任意数据...那么很难避免使用switch语句(至少值得努力尝试)。就代码可读性而言,我建议使用枚举。你可以像这样重构你的代码:
    switch ((ImageTypes)Tab.SelectedIndex)
    {
        case ImageTypes.TypeOne:
            img1.Opacity= 1;
            img2.Opacity = 0.5;
            img3.Opacity  = 0.5;
            break;
        case ImageTypes.TypeTwo:
            img1.Opacity = 0.5;
            img2.Opacity = 1;
            img3.Opacity  = 0.5;
            break;
        case ImageTypes.TypeThree:
            img1.Opacity = 0.5;
            img2.Opacity = 0.5;
            img3.Opacity = 1;
            break;
    }
    public enum ImageTypes
    {
        TypeOne,
        TypeTwo,
        TypeThree
    }

0

这并不会完全删除它,因为除了创建一个庞大的类继承体系外,没有太多可以做的,但对于这种情况来说,这可能有点过度。

相反,您可以像这样减小它的大小:

switch (Tab.SelectedIndex)
{
     img1.Opacity = 0.5;
     img2.Opacity = 0.5;
     img3.Opacity = 0.5;

     case 0:
         img1.Opacity += 0.5;
         break;
     case 1:
         img2.Opacity += 0.5;
         break;
     case 2:
         img3.Opacity += 0.5;
         break;
}

你可以通过方法来减少代码冗余,这样每次想要改变不透明度时就不必到处更改0.5了。(将0.5放入常量中也是一个好习惯):

switch (Tab.SelectedIndex)
{
     SetInitialOpacity(img1);
     SetInitialOpacity(img2);
     SetInitialOpacity(img3);

     case 0:
         IncreaseOpacity(img1);
         break;
     case 1:
         IncreaseOpacity(img2);
         break;
     case 2:
         IncreaseOpacity(img3);
         break;
}

private void SetInitialOpacity(Image image)
{
    image.Opacity = 0.5;
}


private void IncreaseOpacity(Image image)
{
    image.Opacity += 0.5;
}

0

参考"jimmy-keen"所说,我会选择以下方案:

    public static void HighlightImages(int selection, params Image[] images)
    {
        for (int img = 0; img < images.Length; img++)
        {
            images[img].Opacity = (img == selection ? 1 : 0.5);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接