一个电商平台对在其平台之上销售的除了书籍、食品以及药物以外的商品收取 10% 的费用。而对于进口的商品则额外收取 5% 的附加费用。对于平台抽取的费用计算时,舍入的规则是:对于 n% 抽取率,价格为 p的商品, np/100 的值就近舍入到 0.05(如: 7.125 -> 7.15, 6.66 -> 6.70 )。
卖家卖出一些商品后,可以要求平台提供一个清单,列出其卖出的所有商品名称,价格(包括平台抽取费用),以及平台抽取的总费用和商品总价 。
写一个程序可以根据下面的输入值,输出下面的预期的正确的清单 。
要求:
1. 使用面向对象的设计思想进行设计。
2. 请分析猜测未来可能的变化点,考虑程序的扩展性。
(考察重点是面向对象的设计能力 )
输入:
Input 1:
1 book at 12.49
1 music CD at 14.99
1 chocolate bar at0.85
Input 2:
1 imported box ofchocolates at 10.00
1 imported bottleof perfume at 47.50
Input 3:
1 imported bottleof perfume at 27.99
1 bottle of perfumeat 18.99
1 packet ofheadache pills at 9.75
1 box of importedchocolates at 11.25
输出:
Outpu1:
1 book: 12.49
1 music CD: 16.49
1 chocolate bar:0.85
Sales Fee: 1.50
Total: 29.83
Output 2:
1 imported box ofchocolates: 10.50
1 imported bottleof perfume: 54.65
Sales Fee: 7.65
Total: 65.15
Output 3:
1 imported bottleof perfume: 32.19
1 bottle ofperfume: 20.89
1 packet ofheadache pills: 9.75
1 box of importedchocolates: 11.85
namespace Test1 { /// <summary> /// 商品基本信息类 /// </summary> public class Goods { /// <summary> /// 商品名称 /// </summary> public string Name { get; set; } /// <summary> /// 商品类别 /// </summary> public GoodsCategory Category { get; set; } /// <summary> /// 是否进口 /// </summary> public bool IsImported { get; set; } /// <summary> /// 价格 /// </summary> public double Price { get; set; } /// <summary> /// 商品单位 /// </summary> public string Unit { get; set; } /// <summary> /// 卖家 /// </summary> public Vendor Seller { get; set; } } }
商品类别枚举
namespace Test1 { /// <summary> /// 商品类别枚举 /// </summary> public enum GoodsCategory { /// <summary> /// 书籍 /// </summary> Book, /// <summary> /// 食品 /// </summary> Food, /// <summary> /// 药物 /// </summary> Medicine, /// <summary> /// 其它 /// </summary> Other } }
卖家基本信息类
namespace Test1 { /// <summary> /// 卖家基本信息类 /// </summary> public class Vendor { /// <summary> /// 卖家名称 /// </summary> public string Name { get; set; } /// <summary> /// 卖家联系电话 /// </summary> public string Phone { get; set; } } }
某种商品销售情况
namespace Test1 { /// <summary> /// 某种商品销售情况 /// </summary> public class SalesStatus { /// <summary> /// 销售数量 /// </summary> public int Amount { get; set; } /// <summary> /// 电商平台收取费用 /// </summary> public double Fee { get;private set; } /// <summary> /// 总计 /// </summary> public double Total { get; private set; } /// <summary> /// 对应的商品 /// </summary> public Goods SellGoods; public SalesStatus(Goods goods,int amount) { SellGoods = goods; Amount = amount; SetFeeAndTotal(); } /// <summary> /// 计算应收取的费用及总计 /// </summary> private void SetFeeAndTotal() { //单个商品收取的费率 double rate = FeeRate.GetFeeRate(SellGoods); //卖出多个商品应收的费用 Fee = Amount*Utility.Round(rate*SellGoods.Price); //总计=单价*数量+费用 Total = Fee + Amount*SellGoods.Price; } } }
商品收取费率
namespace Test1 { /// <summary> /// 商品收取费率 /// </summary> public class FeeRate { //除了书籍、食品以及药物以外的商品收取10%的费用 public const double BookRate = 0; public const double FoodRate = 0; public const double MedicineRate = 0; public const double OtherRate = 0.1; //进口的商品则额外收取5%的附加费用 public const double ImportedRate = 0.05; /// <summary> /// 获取商品费率 /// </summary> /// <returns>返回该商品应收的费率</returns> public static double GetFeeRate(Goods goods) { //商品费率 double rate = 0; //根据商品类别获取对应的费率 switch (goods.Category) { case GoodsCategory.Book: rate = BookRate; break; case GoodsCategory.Food: rate = FoodRate; break; case GoodsCategory.Medicine: rate = MedicineRate; break; case GoodsCategory.Other: rate = OtherRate; break; } //进口商品收取额外费用 if (goods.IsImported) rate += ImportedRate; return rate; } } }
通用类
using System; namespace Test1 { /// <summary> /// 通用类 /// </summary> public class Utility { /// <summary> /// 数值就近舍入到0.05 /// </summary> public static double Round(double val) { return Math.Ceiling(val * 20) / 20; } } }
销售清单
using System; using System.Collections.Generic; namespace Test1 { /// <summary> /// 销售清单 /// </summary> public class SalesOrder { /// <summary> /// 销售清单明细 /// </summary> public List<SalesStatus> SalesList { get; set; } /// <summary> /// 收取费用合计 /// </summary> public double SalesFee { get; private set; } /// <summary> /// 总计 /// </summary> public double Total { get; private set; } public SalesOrder() { SalesList = new List<SalesStatus>(); } /// <summary> /// 统计 /// </summary> private void CalTotalAndSalesFee() { SalesFee = 0; Total = 0; foreach (var sale in SalesList) { SalesFee += sale.Fee; Total += sale.Total; } } /// <summary> /// 打印销售清单 /// </summary> public void PrintSalesOrder() { CalTotalAndSalesFee(); string result = ""; foreach (var sale in SalesList) { if (string.IsNullOrWhiteSpace(sale.SellGoods.Unit)) result += sale.Amount + " " + sale.SellGoods.Name + ":" + String.Format("{0:N2}", sale.Total) + "\n"; else result += sale.Amount + " " + sale.SellGoods.Unit + " " + sale.SellGoods.Name + ":" + String.Format("{0:N2}", sale.Total) + "\n"; } result += "Sales Fee: " + String.Format("{0:N2}", SalesFee) + "\n"; result += "Total: " + String.Format("{0:N2}", Total) + "\n"; Console.Write(result); } } }
控制台程序入口
using System; namespace Test1 { class Program { static void Main(string[] args) { #region Input1 //卖家张三的销售情况 Vendor zhangsan = new Vendor { Name = "张三" }; //书的基本信息及销售情况 Goods book = new Goods { Name = "book", Category = GoodsCategory.Book, IsImported = false, Price = 12.49, Seller = zhangsan }; SalesStatus bookSalesStatus = new SalesStatus(book, 1); //音乐CD的基本信息及销售情况 Goods musicCD = new Goods { Name = "music CD", Category = GoodsCategory.Other, IsImported = false, Price = 14.99, Seller = zhangsan }; SalesStatus musicCDSalesStatus = new SalesStatus(musicCD, 1); //巧克力棒的基本信息及销售情况 Goods chocolateBar = new Goods { Name = "chocolate bar", Category = GoodsCategory.Food, IsImported = false, Price = 0.85, Seller = zhangsan }; SalesStatus chocolateBarStatus = new SalesStatus(chocolateBar, 1); //生产销售清单 SalesOrder order1 = new SalesOrder(); order1.SalesList.Add(bookSalesStatus); order1.SalesList.Add(musicCDSalesStatus); order1.SalesList.Add(chocolateBarStatus); //输出销售清单 Console.Write("Outpu1:\n"); order1.PrintSalesOrder(); Console.Write("\n"); #endregion Input1 Console.ReadKey(); } } }
二. 设计程序生成货架位
在仓库中为了方便的管理每个货架,我们会为每个货架命名一个编号,这个编号由字母数字和分隔符(除数字和字母的其它字符,例如:- * | # 等)组成,现在需要设计一个程序输入起始值和结束值按照一定规则生成一组货架编号,且一次最多只能生成5000个。
例如:
1、起始值:A-10 结束值:D-15 输出的结果如下:
A-10,A-11,A-12,A-13,A-14,A-15,
B-10,B-11,B-12,B-13,B-14,B-15,
C-10,C-11,C-12,C-13,C-14,C-15,
D-10,D-11,D-12,D-13,D-14,D-15
2、起始值:A-10A*E 结束值:B-15B*G 输出的结果如下:
A-10A*E,A-10A*F,A-10A*G,A-10B*E,A-10B*F,A-10B*G,A-11A*E,A-11A*F,A-11A*G,A-11B*E,A-11B*F,A-11B*G,A-12A*E,A-12A*F,A-12A*G,A-12B*E,A-12B*F,A-12B*G,A-13A*E,A-13A*F,A-13A*G,A-13B*E,A-13B*F,A-13B*G,A-14A*E,A-14A*F,A-14A*G,A-14B*E,A-14B*F,A-14B*G,A-15A*E,A-15A*F,A-15A*G,A-15B*E,A-15B*F,A-15B*G,B-10A*E,B-10A*F,B-10A*G,B-10B*E,B-10B*F,B-10B*G,B-11A*E,B-11A*F,B-11A*G,B-11B*E,B-11B*F,B-11B*G,B-12A*E,B-12A*F,B-12A*G,B-12B*E,B-12B*F,B-12B*G,B-13A*E,B-13A*F,B-13A*G,B-13B*E,B-13B*F,B-13B*G,B-14A*E,B-14A*F,B-14A*G,B-14B*E,B-14B*F,B-14B*G,B-15A*E,B-15A*F,B-15A*G,B-15B*E,B-15B*F,B-15B*G
3、起始值:A10 结束值:B15 输出的结果如下:
A10,A11,A12,A13,A14,A15,B10,B11,B12,B13,B14,B15
输入错误示例:
起始值 | 结束值 | 错误原因 |
A10 | B203 | 起始值和结束值长度不一样 |
A-10 | B*20 | 第二个字符分隔符不一样 |
D-10 | B-20 | 起始值字母D大于结束值字母B |
B-30 | D-10 | 起始值中数字30 大于结束值中数字10 |
A-5 | D-C | 起始值第三个字符是数字,而结束值第三个字符是字母 |
A-0001 | E-1100 | 生成了5500个,超过一次最大5000个的限制 |
货架编号生成器类
using System; using System.Collections.Generic; namespace Test2 { /// <summary> /// 货架编号生成器 /// </summary> public class ShelvesNumGenerator { /// <summary> /// 起始编号 /// </summary> public string StartNum { get; set; } /// <summary> /// 结束编号 /// </summary> public string EndNum { get; set; } /// <summary> /// 生成货架编号的数量 /// </summary> public int NumCount { get; set; } /// <summary> /// 最大货架编号的数量 /// </summary> public const int MaxCount = 5000; /// <summary> /// 错误编号提示信息 /// </summary> public string ErrorMsg { get; set; } /// <summary> /// 分隔符 /// </summary> public char[] Separators = { '-', '*', '|', '#' }; /// <summary> /// 生成的编号列表 /// </summary> public List<string> NumList { get; private set; } public ShelvesNumGenerator(string startNum, string endNum) { StartNum = startNum; EndNum = endNum; NumList=new List<string>(); } /// <summary> /// 生成货架编号列表 /// </summary> public void GenerateShelvesNumList() { //编号拆分,例如A-10B*G|12拆分成["A","-","10","B","*","G","|","12"] List<string> sList = SplitNumToList(StartNum); List<string> eList = SplitNumToList(EndNum); List<string[]> strsList=new List<string[]>(); for (int i = sList.Count-1; i >=0; i--) { string[] strs; //如果元素是数字 int sNum; if (Int32.TryParse(sList[i], out sNum)) { int eNum = Int32.Parse(eList[i]); strs = new string[eNum - sNum + 1]; for (int j = sNum; j <= eNum; j++) { strs[j - sNum] = j.ToString().PadLeft(sList[i].Length, '0'); } } else { //元素是字母或者分隔符的情况 strs = new string[eList[i][0] - sList[i][0] + 1]; for (int j = sList[i][0]; j <= eList[i][0]; j++) { strs[j - sList[i][0]] =((char)j).ToString(); } } strsList.Add(strs); } Recursion(strsList, new Stack<string>()); } /// <summary> /// 递归组合生成货架编号 /// </summary> private void Recursion(List<string[]> list, Stack<string> stack) { if (stack.Count == list.Count) { NumList.Add(string.Join("", stack.ToArray())); } else { string[] strs = list[stack.Count]; foreach (string s in strs) { stack.Push(s); Recursion(list, stack); stack.Pop(); } } } /// <summary> /// 打印货架编号列表 /// </summary> public void PrintShelvesNumList() { Console.WriteLine("共生成"+NumList.Count+"个货架编号:"); NumList.Sort(); Console.WriteLine(string.Join(",",NumList)+"\n"); } /// <summary> /// 检查起始编号和结束编号是否合法 /// </summary> public bool CheckInvalidNum() { if (StartNum.Length != EndNum.Length) { ErrorMsg = "起始编号和结束编号长度不一样"; return false; } //分隔符位置不一致的情况 foreach (var separator in Separators) { int sIdx = StartNum.IndexOf(separator); int eIdx = EndNum.IndexOf(separator); if (sIdx == 0 || eIdx == 0) { ErrorMsg = "编号不能以分隔符开头"; return false; } if (sIdx > 0 || eIdx > 0) { if (sIdx != eIdx) { int idx = sIdx > 0 && eIdx > 0 ? Math.Min(sIdx, eIdx) : sIdx > 0 ? sIdx : eIdx; ErrorMsg = "第" + (idx + 1) + "个字符分隔符不一样"; return false; } } } //起始编号字母大于结束编号字母 //起始值第三个字符是数字,而结束值第三个字符是字母 for (int i = 0; i < StartNum.Length; i++) { if (IsAlphabet(StartNum[i]) && IsAlphabet(EndNum[i])) { if (StartNum[i] > EndNum[i]) { ErrorMsg = "起始编号字母" + StartNum[i] + "大于结束编号字母" + EndNum[i]; return false; } } if (IsAlphabet(StartNum[i]) && IsNumber(EndNum[i])) { ErrorMsg = "起始编号第" + (i + 1) + "个字符是字母,而结束值第" + (i + 1) + "个字符是数字"; return false; } if (IsNumber(StartNum[i]) && IsAlphabet(EndNum[i])) { ErrorMsg = "起始编号第" + (i + 1) + "个字符是数字,而结束值第" + (i + 1) + "个字符是字母"; return false; } } //起始编号中数字大于结束编号中数字 List<string> sInts = GetIntList(StartNum); List<string> eInts = GetIntList(EndNum); for (int i = 0; i < sInts.Count; i++) { if (Convert.ToInt32(sInts[i]) > Convert.ToInt32(eInts[i])) { ErrorMsg = "起始编号中数字" + sInts[i] + "大于结束编号中数字"+ eInts[i]; return false; } } //可以生成的货架编号数量是否超过了最大值 //计算货架数量的算法:A01B,B05C, COUNT=(B-A+1)*(5-1+1)*(C-B+1) NumCount = 1; for (int i = 0; i < StartNum.Length; i++) { if (IsAlphabet(StartNum[i])) { NumCount *= EndNum[i] - StartNum[i] + 1; } } for (int i = 0; i < sInts.Count; i++) { NumCount *= Convert.ToInt32(eInts[i]) - Convert.ToInt32(sInts[i]) + 1; } if (NumCount > MaxCount) { ErrorMsg = "生成了" + NumCount + "超过一次最大" + MaxCount + "个的限制"; return false; } return true; } /// <summary> /// 判断一个字符是否是字母 /// </summary> public bool IsAlphabet(char c) { //字母A-Z的ASCII码是从65-90 if (c >= 'A' && c <= 'Z') return true; else return false; } /// <summary> /// 判断一个字符是否是数字 /// </summary> public bool IsNumber(char c) { //数字0-9的ASCII码是从48-57 if (c >= '0' && c <= '9') return true; else return false; } /// <summary> /// 提取字符串中的int数放入List /// </summary> public List<string> GetIntList(string str) { List<string> myList = new List<string>(); string number = ""; for (int i = 0; i < str.Length; ++i) { if (IsNumber(str[i])) { number += str[i]; if (i == str.Length - 1) { myList.Add(number); } } else { if (!number.Equals("")) { myList.Add(number); number = ""; } } } return myList; } /// <summary> /// 编号拆分,例如A-10B*G|12拆分成["A","-","10","B","*","G","|","12"] /// </summary> public List<string> SplitNumToList(string str) { List<string> myList = new List<string>(); string number = ""; for (int i = 0; i < str.Length; ++i) { if (IsNumber(str[i])) { number += str[i]; if (i == str.Length - 1) { myList.Add(number); } } else { if (!number.Equals("")) { myList.Add(number); number = ""; } myList.Add(str[i].ToString()); } } return myList; } } }
控制台程序入口
using System; namespace Test2 { class Program { static void Main(string[] args) { do { Console.WriteLine("\n请输入起始编号:"); string startNum = Console.ReadLine(); Console.WriteLine("请输入结束编号:"); string endNum = Console.ReadLine(); if (string.IsNullOrWhiteSpace(startNum) || string.IsNullOrWhiteSpace(endNum)) { Console.WriteLine("\n错误信息:起始编号或者结束编号不能为空\n"); } else { //负责生成货架编号的生成器类 //编号中的字母统一转换成大写 ShelvesNumGenerator generator = new ShelvesNumGenerator(startNum.ToUpper(), endNum.ToUpper()); //首先检查输入的编号是否合法 bool isValid = generator.CheckInvalidNum(); if (!isValid) { Console.WriteLine("\n错误信息:" + generator.ErrorMsg + "\n");//输出错误信息 } else { generator.GenerateShelvesNumList();//生成货架编号列表 generator.PrintShelvesNumList();//输出货架编号列表 } } Console.Write("请输入exit退出系统,其它键继续:"); } while (!Console.ReadLine().ToUpper().Contains("EXIT")); } } }