重构CollapsibleSplitter(2)
在上一篇文章中我们把目标倒是定好了,从哪儿开始呢:去掉可恶的if和switch语句、去掉重复语句,还是减少那些长方法的行数?这是个问题,也许唯一能确定的就是先不去碰那个“8像素”限制了。
仔细分析代码,我们会发现导致这个控件中代码高重复率的主要原因在于大批量使用了if和switch。进一步分析,编CollapsibleSplitter兄弟为什么会使用这么多分支语句呢?是这么这么这么的…嘿嘿J:
- 此控件支持五种绘制风格(分别是Mozilla、XP、Win9x、DoubleDots和Lines),代码需要判断用户选择的是那一种风格;
- 此控件支持四种停靠方式(分别是Top、Bottom、Left和Right),代码需要判断用户选择的是那一种停靠方式;
- 在绘制界面、控制行为的时候控件会在多种状态中来回变换。
面向对象语言使用什么来代替这些分支语句?把问题放到脑子里转两圈——嗯,是策略模式(Strategy)和状态机模式(State)。好吧,那下面就使用这两个模式来进行我们的除臭工作吧。
开始前我们先来温习一下这两个模式(从《设计模式迷你手册》中摘取):
Strategy模式
State模式
我们先定义风格系列“策略”。依据方向(横向或纵向)的不同,绘制界面风格的代码也颇为不同,因此我们为基类定义两个不同的绘制函数,分别代表横向绘制和纵向绘制。下面是抽象基类VisualStyle和及其Mozilla绘制风格的子类:
namespace ISAK.WinGui.Controls.Styles
{
abstract class VisualStyle
{
public abstract void DrawVertical(Graphics g, int x, int y, Color lineColor);
public abstract void DrawHorizontal(Graphics g, int x, int y, Color lineColor);
}
class VisualStyleMozilla: VisualStyle
{
public override void DrawVertical(Graphics g, int x, int y, Color lineColor)
{
for (int i = 0; i < 30; i++)
{
// light dot
g.DrawLine(new Pen(SystemColors.ControlLightLight), x, y + (i * 3), x + 1, y + 1 + (i * 3));
// dark dot
g.DrawLine(new Pen(SystemColors.ControlDarkDark), x + 1, y + 1 + (i * 3), x + 2, y + 2 + (i * 3));
// overdraw the background color as we actually drew 2px diagonal lines, not just dots
g.DrawLine(new Pen(lineColor), x + 2, y + 1 + (i * 3), x + 2, y + 2 + (i * 3));
}
}
public override void DrawHorizontal(Graphics g, int x, int y, Color lineColor)
{
for (int i = 0; i < 30; i++)
{
// light dot
g.DrawLine(new Pen(SystemColors.ControlLightLight), x + (i * 3), y, x + 1 + (i * 3), y + 1);
// dark dot
g.DrawLine(new Pen(SystemColors.ControlDarkDark), x + 1 + (i * 3), y + 1, x + 2 + (i * 3), y + 2);
// overdraw the background color as we actually drew 2px diagonal lines, not just dots
g.DrawLine(new Pen(lineColor), x + 1 + (i * 3), y + 2, x + 2 + (i * 3), y + 2);
}
}
}
}
其他绘制风格的子类还有VisualStyleXP、VisualStyleWin9x、VisualStyleDoubleDots、VisualStyleLines,其代码也是直接从OnPaint方法中的相关位置拷贝过去的。
接着,我们为CollapsibleSplitter类添加一个私有成员
private Styles.VisualStyle styleDrawer;
然后,我们修改VisualStyle属性的set方法:
public VisualStyles VisualStyle
{
get{ return this.visualStyle; }
set
{
this.visualStyle = value;
switch (value)
{
case VisualStyles.Mozilla:
styleDrawer = new Styles.VisualStyleMozilla();
break;
case VisualStyles.XP:
styleDrawer = new Styles.VisualStyleXP();
break;
case VisualStyles.Win9x:
styleDrawer = new Styles.VisualStyleWin9x();
break;
case VisualStyles.DoubleDots:
styleDrawer = new Styles.VisualStyleDoubleDots();
break;
case VisualStyles.Lines:
styleDrawer = new Styles.VisualStyleLines();
break;
}
this.Invalidate();
}
}
再把OnPaint方法中的两个switch去掉,上边的那switch系列的一大堆代码改成下面的样子
styleDrawer.DrawVertical(g, x, y, hot ? hotColor : BackColor);
下面那switch系列的一大堆代码改成
styleDrawer.DrawHorizontal(g, x, y, hot ? hotColor : BackColor);
这绘制风格方面的臭味算是暂且搞定了,虽尚有些令人不满意的地方(看看上面的两个hot ? hotColor : BackColor),但还算过得去。嘿嘿,有些细微的工作么,留待最后才搞定好了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构