如何解决碰到的问题
虽然碰到的问题比想象中的要快,但这些问题还是能解决的。
问题一:每个对象不应该总向数据库检索加载信息
这个问题很好解决,只要在每个能进行实例化得对象上添加相应得构造函数就能解决。
比如:
有一个CustCommodity类,它具有属性ID,Name,Description,Price,Discount,CategoryId这些属性。
我们可以这样实例化它们:
CustCommodity cc=new CustCommodity(ID的值)
我们还应该能这样实例化他们:
CustCommodity cc=new CustCommodity(ID,Name,Description,Price,Discount,CategoryId)
前者是是向CustCommodity类提供一个ID值,让他在实例化得过程中对数据库进行相应的查询,将返回的结果赋予Name,Description,Price,Discount,CategoryId这些属性。
后者则是不需要在实例化得过程中查询数据库,可以让调用者主动提供这些必须得属性。这种方案更适合解决要返回一大批CustCommodity类对象的时候。可以对数据库进行一次查询,使用得到的查询结果实例化这些CustCommodity类。
问题二:一个功能不应该依靠两个类来解决
一个功能应该由一个类来解决,这样维护和编码都会自然而然的少去很多的麻烦。
在我们的设计中,不应该存在派生类需要依赖父类,并且父类又依然派生类这种现象。在数据库的设计阶段,我们有意识的将父类的相关属性划为一张表,而将它的派生类划为另一张表。这样设计如果有新的派生类出现的话,我们只需扩张现有的表,而不需要修改现有的表。
但这样的设计也存在弊端,在向数据库中插入某一派生类的记录的时候需要进行2次插入操作。首先在父类中插入一条记录,然后在派生类所对应的表中插入一条记录。
比如现在要录入类CustCommodity的记录,它具有的属性为:Name,Description,Price,Discount,CategoryId。数据的插入逻辑可以这么设计:
create procedure [dbo].[InsertCustCommodity] ( @OutCommId bigint output, @OutCustCommId bigint output, @Name nvarchar(100), @Price money, @Description nvarchar(1000), @Discount float, @CategoryId uniqueidentifier ) as begin declare @errorSum int begin transaction insert into Commodity values(@Name,@Price,@Description,@Discount,@CategoryId) set @OutCommId=@@identity set @errorSum=@errorSum+@@error insert into CustCommodity(CommodityId) values(@OutCommId) set @OutCustCommId=@@identity set @errorSum=@errorSum+@@error if(@errorSum<>0) begin set @OutCommId=-1 set @OutCustCommId=-1 rollback transaction end else commit transaction end
我们将调用这个存储过程的方法设计在CustCommodity类中,以后如果有相应的派生类出现,只要自行设计相应的数据插入算法就能解决问题,而不用去修改父类的代码。
CustCommodity类中与其对应的插入算法如下:
private bool AddCustCommodity(string name, double price, string description, float discount, Guid categoryid) { int result = 0; SqlParameter[] parameters=new SqlParameter[]{ new SqlParameter("@OutCommId",SqlDbType.BigInt), new SqlParameter("@OutCustCommId",SqlDbType.BigInt), new SqlParameter("@Name",SqlDbType.NVarChar,100), new SqlParameter("@Price",SqlDbType.Money), new SqlParameter("@Description",SqlDbType.NVarChar,1000), new SqlParameter("@Discount",SqlDbType.Float), new SqlParameter("@CategoryId",SqlDbType.UniqueIdentifier) }; parameters[0].Direction = ParameterDirection.Output; parameters[1].Direction = ParameterDirection.Output; parameters[2].Value = name; parameters[3].Value = price; parameters[4].Value = description; parameters[5].Value = discount; parameters[6].Value = categoryid; result = SqlHelper.ExecuteNonQuery(SqlHelper.conn, CommandType.StoredProcedure, "InsertCustCommodity", parameters); if (result > 0) { base.LoadCommodity( long.Parse(parameters[0].Value.ToString()), name, price, description, discount, categoryid ); this._ID = long.Parse(parameters[1].Value.ToString()); }
问题三:多余的类
商品类目与信息类目都是多余的类目,去掉他们可以大大简化软件的复杂度。我们可以将它们合并为一个类:Category类。这样的设计不是与问题二矛盾吗?一个类具有二个职责。
但是我们可以使用些技巧来大大降低这样做的缺点,并且我们又能将重要的代码进行复用。
类目主要的功能就是返回在该类目下的成员,当初把类目设计成商品类目与信息类目就是应为认为他们是两个不同的对象。我们在类目中加入方法getCommodities和方法getMessages。通过他们来返回商品和信息。
对这两个方法应用工厂方法,可以把将来会改变的内容放在类的外面,与现有的代码隔离开来。
设计如下:
Class Category{ private static IGetCommoditiesFactory _CFactory=null; //工厂方法用来根据具体需要获取具体的sql语句 private static IGetMessagesFactory _MFactory = null; public static IGetCommoditiesFactory GetCommoditiesFactory { get { return _CFactory; } set { _CFactory = value; } } public static IGetMessagesFactory GetMessagesFactory { get { return _MFactory; } set { _MFactory = value; } } public DataSet getCommodities(CommodityType type) { if (_CFactory == null) throw new Exception("请提供获取商品的工厂类"); DataSet ds; string selectCmd = _CFactory.GetCommodity(type);//执行工厂方法获取需要的sql SqlParameter[] parameters = new SqlParameter[]{ new SqlParameter("@CategoryId",SqlDbType.UniqueIdentifier) }; parameters[0].Value = _ID; ds = SqlHelper.ExecuteDataset(SqlHelper.conn, CommandType.Text, selectCmd, parameters); return ds; } public DataSet getMessages(MessageType type) { DataSet ds; string selectCmd = _MFactory.GetMessages(type); SqlParameter[] parameters = new SqlParameter[]{ new SqlParameter("@MessageCategoryId",SqlDbType.UniqueIdentifier) }; parameters[0].Value = _ID; ds = SqlHelper.ExecuteDataset(SqlHelper.conn, CommandType.Text, selectCmd, parameters); return ds; } ……………………………………..//其他的省略 } public interface IGetCommoditiesFactory { string GetCommodity(CommodityType type); } public interface IGetMessagesFactory { string GetMessages(MessageType type); } public class GetCommodityFactory:IGetCommoditiesFactory { public GetCommodityFactory() { // // TODO: 在此处添加构造函数逻辑 // } #region IGetCommoditiesFactory 成员 public string GetCommodity(CommodityType type) { string selectCmd=string.Empty; switch (type) { case CommodityType.CustCommodity: selectCmd = "select cc.Id,c.Price,c.Name,c.Description,c.Discount,c.CategoryId from Commodity c inner join CustCommodity cc on c.Id=cc.CommodityId where " + " c.CategoryId=@CategoryId"; break; default: break; } return selectCmd; } #endregion } public class GetMessageFactory:IGetMessagesFactory { public GetMessageFactory() { // // TODO: 在此处添加构造函数逻辑 // } #region IGetMessagesFactory 成员 public string GetMessages(MessageType type) { string result = string.Empty; switch(type) { case MessageType.Discount: result = "select dm.Id,m.Caption,m.Context,m.Author,m.CreateDate,m.MessageCategoryId,dm.DeadLine from Message m inner join DisCountMessage dm " + " on m.Id=dm.MessageId where m.MessageCategoryId=@MessageCategoryId"; break; default: break; } return result; } #endregion }
这样一来就能把会经常需要修改的代码隔离与不需要经常修改的代码隔离起来。