看<<Head First设计模式>>有一段时间了,感觉总是这样看的话收获并不多,由于并没有在实践中应用所以才会比较枯燥,容易看了就忘.并没有实际掌握.就象以前看其它的设计模式的书一样,看了就只是看了,最后回忆起来只是几个模式的大致样子.所以从今天开始打算结合在开发过程中遇到的相应的模式,记录下来,深入理解.
声明一下:只能保证原创和认真思考.不敢保证质量和水平,但求能够给看的人带来收获.
策略模式(Strategy Pattern)属于对象行为型模式,体现了两个非常基本的面向对象设计的基本原则:封装变化的概念;编程中使用接口,而不是对接口实现。
定义:
定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。每一个算法封装到具有共同接口的独立的类中,策略模式使这些算法在客户端调用它们的时候能够互不影响地变化。
结构:
应用:
在asp.net的分层的框架开发中,经常需要在某一层使用和另外一个层次相关的东西,这样造成了耦合,对应用程度的扩展性造成了影响.前段时间在开发产品的时候,在业务层经常需要获取或者是保存当前用户信息的操作,这样的操作如果在Controller层的话还好,在业务层就有些头疼.毕竟整个产品框架是想业务层不能局限于WebFrom,存储方式也不能局限于Session,Cookie或者是静态变量.(
注:遇到这样的问题,最好是先考虑了是不是设计出了问题).
NewOrder
public Order NewOrder()
{
var order =new Order();
order.OrderID = Guid.NewGuid().ToString();
//读取状态(采购商和供应商)
order.ManufacturerReadState =0;
order.InnerCompanyReadState =0;
User currentUser = Session["CurrentUser"] as User;
InnerCompany ic = currentUser.Company as InnerCompany;
order.DeliveryLoc = currentUser.Company.ReceiveAd;
//默认为当前用户
order.Buyer = order.Creator = currentUser;
order.OrderState = OrderState.NewOrder;
return order;
}
一开始我们用了一个看起来很笨的方法,那就是传递UserID到业务层,但是这也造成了业务层很乱了,因为几乎每个业务方法都需要有个UserID参数...
NewOrder
public Order NewOrder(string UserID)
{
var order =new Order();
order.OrderID = Guid.NewGuid().ToString();
//读取状态(采购商和供应商)
order.ManufacturerReadState =0;
order.InnerCompanyReadState =0;
User currentUser = UserManager.GetUser(UserID);
InnerCompany ic = currentUser.Company as InnerCompany;
order.DeliveryLoc = currentUser.Company.ReceiveAd;
//默认为当前用户
order.Buyer = order.Creator = currentUser;
order.OrderState = OrderState.NewOrder;
return order;
}
要解决的重点就是要把对用户操作和具体的应用环境解耦,并且又不要对应用程序造成比较大的修改和影响.策略模式的解决方案:(此解决方案源自spring.net)
在IThreadStorage中定义了操作数据的接口(抽象策略对象),会有三个具体的实现CallContextStorage, HybridContextStorage和ThreadStaticStorage(具体策略对象),他们采用不同的方式来实现实现了接口.在LogicalThreadContext(环境对象)中,只引用抽象接口来完成对数据的操作.具体的实现方式对外面是封闭的.我们在开发中在LogicalThreadContext里直接创建了使用的具体策略对象,其实这里可以根据需要做成可配置,或者是利用ioc来实现更灵活些.
代码实现:
IThreadStorage.cs
publicinterface IThreadStorage
{
///<summary>
/// Retrieves an object with the specified <paramref name="name"/>.
///</summary>
///<param name="name">The name of the item.</param>
///<returns>
/// The object in the current thread's context associated with the
/// specified <paramref name="name"/> or null if no object has been stored previously
///</returns>
object GetData(string name);
///<summary>
/// Stores a given object and associates it with the specified <paramref name="name"/>.
///</summary>
///<param name="name">The name with which to associate the new item.</param>
///<param name="value">The object to store in the current thread's context.</param>
void SetData(string name, object value);
///<summary>
/// Empties a data slot with the specified name.
///</summary>
///<remarks>
/// If the object with the specified <paramref name="name"/> is not found, the method does nothing.
///</remarks>
///<param name="name">The name of the object to remove.</param>
void FreeNamedDataSlot(string name);
}
CallContextStorage.cs
///<summary>
/// Implements <see cref="IThreadStorage"/> by using <see cref="CallContext"/>.
///</summary>
///<author>Erich Eichinger</author>
publicclass CallContextStorage : IThreadStorage
{
///<summary>
/// Retrieves an object with the specified name.
///</summary>
///<param name="name">The name of the item.</param>
///<returns>The object in the call context associated with the specified name or null if no object has been stored previously</returns>
publicobject GetData(string name)
{
return CallContext.GetData(name);
}
///<summary>
/// Stores a given object and associates it with the specified name.
///</summary>
///<param name="name">The name with which to associate the new item.</param>
///<param name="value">The object to store in the call context.</param>
publicvoid SetData(string name, object value)
{
CallContext.SetData(name, value);
}
///<summary>
/// Empties a data slot with the specified name.
///</summary>
///<param name="name">The name of the data slot to empty.</param>
publicvoid FreeNamedDataSlot(string name)
{
CallContext.FreeNamedDataSlot(name);
}
}
LogicalThreadContext.cs
///<summary>
/// An abstraction to safely store "ThreadStatic" data.
///</summary>
///<remarks>
/// By default, <see cref="CallContext"/> is used to store thread-specific data.
/// You may switch the storage strategy by calling <see cref="SetStorage(IThreadStorage)"/>.<p/>
///<b>NOTE:</b> Access to the underlying storage is not synchronized for performance reasons.
/// You should call <see cref="SetStorage(IThreadStorage)"/> only once at application startup!
///</remarks>
///<author>Erich Eichinger</author>
publicsealedclass LogicalThreadContext
{
///<summary>
/// Holds the current <see cref="IThreadStorage"/> strategy.
///</summary>
///<remarks>
/// Access to this variable is not synchronized on purpose for performance reasons.
/// Setting a different <see cref="IThreadStorage"/> strategy should happen only once
/// at application startup.
///</remarks>
privatestatic IThreadStorage threadStorage =new CallContextStorage();
///<summary>
/// Set the new <see cref="IThreadStorage"/> strategy.
///</summary>
publicstaticvoid SetStorage(IThreadStorage storage)
{
AssertUtils.ArgumentNotNull(storage, "storage");
threadStorage = storage;
}
private LogicalThreadContext()
{
thrownew NotSupportedException("must not be instantiated");
}
///<summary>
/// Retrieves an object with the specified name.
///</summary>
///<param name="name">The name of the item.</param>
///<returns>The object in the context associated with the specified name or null if no object has been stored previously</returns>
publicstaticobject GetData(string name)
{
return threadStorage.GetData(name);
}
///<summary>
/// Stores a given object and associates it with the specified name.
///</summary>
///<param name="name">The name with which to associate the new item.</param>
///<param name="value">The object to store in the current thread's context.</param>
publicstaticvoid SetData(string name, object value)
{
threadStorage.SetData(name, value);
}
///<summary>
/// Empties a data slot with the specified name.
///</summary>
///<param name="name">The name of the data slot to empty.</param>
publicstaticvoid FreeNamedDataSlot(string name)
{
threadStorage.FreeNamedDataSlot(name);
}
}
这样,我们在Application_BeginRequest方法中记录当前用户,然后在业务层的一个方法里,获取当前用户:
Application_BeginRequest
protectedvoid Application_BeginRequest(Object sender, EventArgs e)
{
if (CookieHelper.UserID !=null)
{
if(LogicalThreadContext.GetData("UserID") ==null)
LogicalThreadContext.SetData("UserID", CookieHelper.UserID);
}
}
BaseManager
publicabstractclass BaseManager
{
publicstring CurrentUserID
{
get
{
object contextData = LogicalThreadContext.GetData("UserID");
if (contextData ==null)
returnnull;
else
return contextData.ToString();
}
}
}
其它的两个实现和类图(及enterprise architect文件),请下载