从零开始实现一个电子商务网站----错误比想象中来得更快(五)

错误比想象中来的更快

在设计完数据库后,该编码实现UML设计中的类结构。怀着激动心情的我,马上就开始了编码实现工作。但问题总是悄悄的跟着你后面,然后在关键时候档住你去路,让你进退两难。

问题一:每个对象不应该总向数据库检索加载信息

从零开始实现一个电子商务网站----错误比想象中来得更快(五)

__Commodity

从零开始实现一个电子商务网站----错误比想象中来得更快(五)

__CustCommodity

CustCommodityCommodity的派生类,这两类的大多数方法都没问题。问题出在他们的构造函数以及他们的私有方法。

Commodity类:

构造函数1:商品(编号:Long)

构造函数2:商品(名称 : String, 价格 : Double, 文字描述 : String, 图片描述 : ArrayList, 折扣率 : float, 所属类目 : 商品类目)

在设计Commodity类时只考虑到了2种构造情况:

1通过给出商品编号来加载已有商品。

2通过赋予新数据来添加新的商品。

CustCommodity类:

构造函数1: 可选某些特征的商品(编号 : long)

构造函数2: 可选某些特征的商品(名称 : String, 价格 : Double, 文字描述 : String, 图片描述 : ArrayList, 折扣率 : float, 所属类目 : 商品类目, 可选特征集合 : ArrayList)

这两个构造函数的功能与Commodity相同,加载和添加数据。

但是类CommodityCustCommodity却缺少了1个非常关键的构造函数,负责拷贝数据的构造函数(将一个类需要的所有属性通过这个构造函数来对它进行赋予)。如果没有这个构造函数,那只能一个一个的加载商品。加载一个就要对数据库进行一次查询,这样的设计是糟糕的。期望中的行为是向数据查询一次,然后能实例化多个商品类。比如:向数据库请求一个DataSet,然后通过遍历这个DataSet中的数据来实例化多个商品类。

问题二:一个功能不应该依靠两个类来解决

在类Commodity中不应该有负责添加数据的方法。

Commodity类中的方法:添加该商品(名称 : String, 价格 : Double, 文字描述 : String, 图片描述 : ArrayList, 折扣率 : float, 所属类目 : 商品类目)

应该去掉,这个方法对Commodity来说并不合适。去掉这个方法后会大幅降低软件的复杂度。

在设计数据库的时候Commodity是一张单独的表格,从它派生的类在数据库中各自都会有张表并且他们与Commodity表存在外键关联。

在设计的时候我觉得各个类应该封装好自己对数据库的操作,即在数据库中的Commodity表只能由类Commodity来控制它的数据,别的不行。

举个简单的例子就能发现这样的设计并不合理:

我们要添加一个CustCommodity对象

CustCommodity cc=new CustCommodity(string name, double price, string description, ArrayList picturecollection,

float discount, Guid categoryid)

CustCommodity类会怎么做能?

首先调用Commodity的构造函数

Commodity c=new Commodity(string name, double price, string description, ArrayList picturecollection, float discount, Guid categoryid)

如果Commodity构造成功,CustCommodity类会对数据库中的表格CustCommodity录入数据。

为了保证数据能正确的添加到数据库中,我又不得不使用事务。它们的实现代码如下:

public class Commodity { private long _ID = -1; private string _Name; private double _Price; private string _Description; private ArrayList _PicCollection; private float _Discount; private Guid _CategoryID; public CommodityType Type; public Commodity() { Type = CommodityType.Commodity; } public Commodity(long id) { LoadCommodity(id); } public Commodity(long id, SqlTransaction transaction) { } public Commodity(string name, double price, string description, ArrayList picturecollection, float discount, Guid categoryid) { //将这些信息添加到数据库,添加成功就为这些字段赋值 } public Commodity(string name, double price, string description, ArrayList picturecollection, float discount, Guid categoryid, ref SqlTransaction transaction) { AddCommodity(name, price, description, picturecollection, discount, categoryid, ref transaction); } #region 属性 public long ID { get { return _ID; } } ……………. #endregion public static abstract void DelCommodity(long id); public static abstract void DelCommodity(Commodity commodity); protected void AddCommodity(string name, double price, string description, ArrayList picturecollection, float discount, Guid categoryid, ref SqlTransaction tran) { SqlParameter[] parameters = new SqlParameter[]{ new SqlParameter("@id", SqlDbType.BigInt), new SqlParameter("@name",SqlDbType.NVarChar,50), new SqlParameter("@price",SqlDbType.Money), new SqlParameter("@description",SqlDbType.NVarChar,1000), new SqlParameter("@discount",SqlDbType.Float), new SqlParameter("commcateid",SqlDbType.UniqueIdentifier) }; parameters[0].Direction = ParameterDirection.Output; parameters[1].Value = name; parameters[2].Value = price; parameters[3].Value = description; parameters[4].Value = discount; parameters[5].Value = categoryid; SqlCommand cmd = SqlHelper.CreateSqlCommand("InsertCommodity", CommandType.StoredProcedure, parameters, ref tran); try { cmd.ExecuteNonQuery(); } catch { tran.Rollback(); throw new Exception("Commodity初始化失败!"); } _ID = long.Parse(parameters[0].Value.ToString()); _Name = name; _Price = price; _Description = description; _PicCollection = picturecollection; _Discount = discount; _CategoryID = categoryid; } protected void LoadCommodity(long id) { SqlParameter[] parameters = new SqlParameter[] { new SqlParameter("@id", SqlDbType.BigInt) }; parameters[0].Value = id; SqlDataReader reader; reader = SqlHelper.ExecuteReader(SYSTEM.CONNECTSTRING, "select * from commodity where [email protected]", CommandType.Text, parameters); while (reader.Read()) { _ID = long.Parse(reader["Id"].ToString()); _Name = reader["Name"].ToString(); _Description = reader["Description"].ToString(); _Discount = float.Parse(reader["Discount"].ToString()); _Price = double.Parse(reader["Price"].ToString()); _CategoryID = new Guid(reader["CommCateID"].ToString()); } reader.Close(); } } public class CustCommodity : Commodity { private SqlTransaction tran = null; private long _ID; public long ID { get { return _ID; } } public CustCommodity() { Type = CommodityType.CustCommodity; } public CustCommodity(long id) { _ID = id; SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["xy_1"].ToString()); SqlCommand cmd = new SqlCommand("Select @CommodityId=CommodityId from CustCommodity where [email protected]", conn); SqlParameter[] parameters = new SqlParameter[]{ new SqlParameter("@CommodityId", SqlDbType.BigInt), new SqlParameter("@id", SqlDbType.BigInt) }; parameters[0].Direction = ParameterDirection.Output; parameters[1].Value = id; cmd.Parameters.AddRange(parameters); conn.Open(); cmd.ExecuteNonQuery(); conn.Close(); base.LoadCommodity(long.Parse(parameters[0].Value.ToString())); } public CustCommodity(string name, double price, string description, ArrayList picturecollection, float discount, Guid categoryid) { if (!CreateTransaction()) { throw new Exception("CustCommodity初始化失败!"); } base.AddCommodity(name, price, description, picturecollection, discount, categoryid, ref tran); if (base.ID != -1) { SqlCommand cmd = new SqlCommand("InsertCustCommodity", tran.Connection, tran); cmd.CommandType = CommandType.StoredProcedure; SqlParameter[] parameters = new SqlParameter[]{ new SqlParameter("@CustCommodityId",SqlDbType.BigInt), new SqlParameter("@CommodityId",SqlDbType.BigInt) }; parameters[0].Direction = ParameterDirection.Output; parameters[1].Value = base.ID; cmd.Parameters.AddRange(parameters); try { cmd.ExecuteNonQuery(); } catch { tran.Rollback(); throw new Exception("CustCommodity初始化失败!"); } _ID = long.Parse(parameters[0].Value.ToString()); tran.Commit(); } } public static void DelCustCommodity(long id) { Commodity.DelCommodity(id); } public static void DelCustCommodity(CustCommodity custcommodity) { Commodity.DelCommodity(custcommodity); } private bool CreateTransaction(string connectionString) { bool result = false; try { SqlConnection conn = new SqlConnection(connectionString); conn.Open(); tran = conn.BeginTransaction(); } catch { } finally { if (conn.State == ConnectionState.Open) conn.Close(); } result = true; return result; } }

添加一条CustCommodity记录,程序要在Commodity类与CustCommodity类间通过事务来游走,这样的设计真糟糕,维护起来简直就是恶梦。应该做到一个任务只由一个类去完成。我们应该去掉父类Commodity中的AddCommodity()方法,让所有的数据录入过程由子类来完成。

同样的道理父类中的LoadCommodity方法与父类中的DelCommodity静态方法都应该去掉,让所有的数据操作步骤都由他的派生类完成。以后如果有新的派生类则只要添加相对与它的添加,删除,加载算法。

问题三:多余的类

在现在的设计中存在些多余的类:商品类目类,信息类目类。在设计的时候设计这两个类是为了能更好的区分商品与信息的区别。但这两个类完成的工作基本一样,如果将他们并成一个类的化,将会简化软件的复杂度。

问题四:错误的类依赖

在网站总体类图中,订单类依赖购买项类。这样的依赖是不可取的,因为我们并不想永久性的存储购买项。再众多的购买项中能被顾客选择并提交为订单的只占少数。很多时候在顾客提交订单后,我们将会清除他之前所选择的购买项。所以我们要另外设计一张表格来保存订单中包含的商品(如图_表格_OrderDetails

从零开始实现一个电子商务网站----错误比想象中来得更快(五)

_表格_OrderDetails

很简单,只有3个字段。在后续阶段我将会把一些功能逐步的添加上去,刚开始就搭建个骨架吧。