Xamarin开发:商场促销(策略设计模式)
- 一、介绍
- 二、需求分析
- 三、实现
- 四、需求分析
- 问题1解决方案
- 问题2解决方案
- 五、增加新需求
- 六、代码优化与分析
- 总结
一、介绍
本文引用《大话设计模式》第二章节的内容进行学习分析,仅供学习使用
这里接着我上一篇 Xamarin开发:基础 继续编写
这里采用策略设计模式进行编写,所谓的代码设计并不是单单指某一种的设计模式而已,比如我们接下来要介绍的策略设计模式实际上是基于简单工厂设计模式的改造而来的,所以在设计代码逻辑的时候不要忠于代码一定要使用某一种设计模式来实现,而应该是应该将需求细化,具体分析适合哪一种设计模式,我所认为的设计模式实际上就是将我们平时比较复杂的代码规范化、降低代码的重复性、冗余性的表现,让我们的代码更加的简单,让其他程序看到感觉之后感觉逻辑简单,代码清晰,这也是作为一名程序员应该具有的能力。
二、需求分析
先看一下需求,如下图
这里我们先抛开 计算方式 这一问题
仅仅实现简单的 输入单价、输入数量,点击确认按钮计算出在 总计,并在TextView中显示,单价: xx 数量: xx 总计: xx
并且点击重置,有重置功能即可
这里对于刚开始接触编程的人来说可能分析并不会透彻,只为来实现功能而去实现,但是不要紧我们可以一步步的优化将我们的代码进行规范化,让别人看着思路清晰。
三、实现
这里就不在讲述我的点击时间和Label属性是如何注册和调用的了,如果不知道的可以看我上一篇文章
打开Vs 中的 ViewController.cs脚本
这是没有编写逻辑的脚本
namespace ShoppingMall
{
public partial class ViewController : NSViewController
{
public ViewController(IntPtr handle) : base(handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Do any additional setup after loading the view.
}
public override NSObject RepresentedObject
{
get
{
return base.RepresentedObject;
}
set
{
base.RepresentedObject = value;
// Update the view, if already loaded.
}
}
}
这里将 确认 和 点击 按钮的事件注册一下
/// <summary>
/// Makes the sure.确认按钮
/// </summary>
/// <param name="sender">Sender.</param>
partial void MakeSure(NSButton sender)
{
throw new NotImplementedException();
}
/// <summary>
/// Resert the specified sender.重置按钮
/// </summary>
/// <param name="sender">Sender.</param>
partial void Resert(NSButton sender)
{
throw new NotImplementedException();
}
编写逻辑如下
/// <summary>
/// The m price.单价
/// </summary>
private double m_price = 0d;
/// <summary>
/// The m amount.数量
/// </summary>
private double m_amount = 0d;
/// <summary>
/// The m totalmoney.未折扣价格
/// </summary>
private double m_totalmoney = 0d;
/// <summary>
/// Makes the sure.确认按钮
/// </summary>
/// <param name="sender">Sender.</param>
partial void MakeSure(NSButton sender)
{
if (string.IsNullOrEmpty(PriceField.StringValue) || string.IsNullOrEmpty(AmountField.StringValue))
return;
m_price = double.Parse(PriceField.StringValue);
m_amount = double.Parse(AmountField.StringValue);
m_totalmoney = m_price * m_amount;
TotalPrice.StringValue = m_totalmoney.ToString();
TextView.Value = "单价:" + m_price + " 数量:" + m_amount + " 正常收费 合计:" + m_totalmoney;
}
/// <summary>
/// Resert the specified sender.重置按钮
/// </summary>
/// <param name="sender">Sender.</param>
partial void Resert(NSButton sender)
{
PriceField.StringValue = string.Empty;
AmountField.StringValue = string.Empty;
TextView.Value = string.Empty;
TotalPrice.StringValue = "0.00";
}
功能如下
四、需求分析
这里我们来分析一下上述代码:
问题1:代码中用到了很多double.Parse() 怎么解决?
问题2:这时候上面又提出新的需求,我们要采用一个打折处理,如何处理比较妥当?
问题1解决方案
这里编写一个工具类,直接string.ToDouble()即可
public static class Tools
{
public static double ToDouble(this string str)
{
double dbl = 0d;
double.TryParse(str,out dbl);
return dbl;
}
}
问题2解决方案
这里我们添加一个下拉菜单即可(Pop Up Button)
这里需要初始化一下下拉菜单的选项
这里在编写一个Const类,里面存放下拉列表选项:
public class Const
{
public static string[] PullDown = { "请选择计费方式", "正常收费", "八折" };
}
注册下拉列表事件并写一个初始化的方法:
/// <summary>
/// Init this instance.初始化
/// </summary>
public void Init()
{
PullDown.RemoveAllItems();
PullDown.AddItems(Const.PullDown);
}
/// <summary>
/// Drops down.下拉列表选择
/// </summary>
/// <param name="sender">Sender.</param>
partial void DropDown(NSPopUpButton sender)
{
PullDown.Title = sender.TitleOfSelectedItem;
}
这里的Init方法放在加载窗口之后直接执行
public override void ViewDidLoad()
{
base.ViewDidLoad();
Init();
// Do any additional setup after loading the view.
}
这里分析一下打折处理,我们可以把打折 正常收费作为一个对象处理,然后写一个抽象类让我们创建的对象去继承,最后写一个简单工厂去实例化对象,从而降低了代码的耦合性,使其后续的修改不会影响到其他类;
修改一下点击事件最后代码如下:
/// <summary>
/// Cash super.抽象类
/// </summary>
public abstract class CashSuper
{
public abstract double AcceptCash(double money);
}
/// <summary>
/// Cash normal.正常
/// </summary>
public class CashNormal : CashSuper
{
public override double AcceptCash(double money)
{
return money;
}
}
/// <summary>
/// Cash rebate.打折
/// </summary>
public class CashRebate : CashSuper
{
private double moneyRebate = 1d;
public CashRebate(string moneyRebate)
{
this.moneyRebate = double.Parse(moneyRebate);
}
public override double AcceptCash(double money)
{
return money * moneyRebate;
}
}
简单工厂:
public class CashFactory
{
public static CashSuper CreateCashAccept(string type)
{
CashSuper super = null;
switch (type)
{
case "正常收费":
super = new CashNormal();
break;
case "八折":
super = new CashRebate("0.8");
break;
default:
break;
}
return super;
}
}
点击事件修改
/// <summary>
/// The discount.折扣价
/// </summary>
private double m_discount = 0d;
/// <summary>
/// Makes the sure.确认按钮
/// </summary>
/// <param name="sender">Sender.</param>
partial void MakeSure(NSButton sender)
{
if (string.IsNullOrEmpty(PriceField.StringValue) || string.IsNullOrEmpty(AmountField.StringValue))
return;
m_price = PriceField.StringValue.ToDouble();
m_amount = AmountField.StringValue.ToDouble();
m_totalmoney = m_price * m_amount;
//简单工厂模式
CashSuper super = CashFactory.CreateCashAccept(PullDown.Title);
m_discount = super.AcceptCash(m_totalmoney);
TotalPrice.StringValue = m_discount.ToString();
TextView.Value = "单价:" + m_price + " 数量:" + m_amount + " 合计:" + m_discount;
}
五、增加新需求
这时候我们增加了一个满减的活动 满300减100
这时候我们分析,只需要在增加一个满减类即可,并且修改一下简单工厂就可以完美解决
新增类:
/// <summary>
/// Cash return.返利
/// </summary>
public class CashReturn : CashSuper
{
private double moneyCondition = 0d;
private double moneyReturn = 0d;
public CashReturn(string moneyCondition,string moneyReturn)
{
this.moneyCondition = double.Parse(moneyCondition);
this.moneyReturn = double.Parse(moneyReturn);
}
public override double AcceptCash(double money)
{
if (money >= moneyCondition)
return money - Math.Floor(money / moneyCondition) * moneyReturn;
return money;
}
}
简单工厂类修改:
public class CashFactory
{
public static CashSuper CreateCashAccept(string type)
{
CashSuper super = null;
switch (type)
{
case "正常收费":
super = new CashNormal();
break;
case "八折":
super = new CashRebate("0.8");
break;
case "满300减100":
super = new CashReturn("300","100");
break;
default:
break;
}
return super;
}
}
功能如下:
六、代码优化与分析
这里我们看一下 确认按钮 的点击事件中的逻辑
现在我们获取结果的时候我们需要调用两个类一个是抽象类,另一个是工厂类,这样不利于代码的清晰度,这时候我们需要将简单工厂与策略模式结合一下,让我们的代码更加简洁。
如下:
public class CashContext
{
private CashSuper m_cashsuper = null;
public CashContext(string type)
{
switch (type)
{
case "正常收费":
m_cashsuper = new CashNormal();
break;
case "八折":
m_cashsuper = new CashRebate("0.8");
break;
case "满300减100":
m_cashsuper = new CashReturn("300", "100");
break;
default:
m_cashsuper = null;
break;
}
}
public double GetResult(double money)
{
return m_cashsuper.AcceptCash(money);
}
}
点击事件修改:
/// <summary>
/// Makes the sure.确认按钮
/// </summary>
/// <param name="sender">Sender.</param>
partial void MakeSure(NSButton sender)
{
if (string.IsNullOrEmpty(PriceField.StringValue) || string.IsNullOrEmpty(AmountField.StringValue))
return;
m_price = PriceField.StringValue.ToDouble();
m_amount = AmountField.StringValue.ToDouble();
m_totalmoney = m_price * m_amount;
简单工厂模式
//CashSuper super = CashFactory.CreateCashAccept(PullDown.Title);
//m_discount = super.AcceptCash(m_totalmoney);
//简单工厂与策略模式结合
CashContext cc = new CashContext(PullDown.Title);
m_discount = cc.GetResult(m_totalmoney);
TotalPrice.StringValue = m_discount.ToString();
TextView.Value = "单价:" + m_price + " 数量:" + m_amount + " 合计:" + m_discount;
}
这样使得我们的代码更加的简洁,对一个类进行操作即可
UML类图:
总结
实际上策略模式实际上是一种定义一系列算法的方法,概念来讲所有的算法都是实现相同的工作,只是实现不一样,它可以以相同的方式调用所有算法,减少了各种算法类与算法类之间的耦合度。
好处:简化了单元测试,每个算法都有自己的类,可以通过自身的接口进行单独测试,不影响其他类。
注意:策略模式是封装各种算法,所以避免不了很多的判断,那么这个时候不管是if、switch其实都不是最好的选择,我们那可以选择使用反射来进行,反射在之后的博客中讲解。