我正在用c#实现我的控制台应用程序的超时。我有一个名为MySession的类,它必须由会话超时绑定。这意味着在类的构造函数中,我为创建的对象配置了超时。如果在指定的超时时间内没有调用类中的任何方法,则在后续访问任何方法时,必须抛出timeout异常。
这是该类的虚拟实现。
public class MySession {
private TimeSpan timeout;
private DateTime lastAccessedTime;
public MySession(TimeSpan timeout) {
this.timeout = timeout;
lastAccessedTime = DateTime.Now;
}
public void StoreName(string name) {
TimeSpan diff = DateTime.Now - lastAccessedTime;
if(diff.Ticks - timeout.Ticks > 0) {
throw new TimeoutException("session timed out");
}
//store logic
lastAccessedTime = DateTime.Now;
}
public void StoreAddress(string address) {
TimeSpan diff = DateTime.Now - lastAccessedTime;
if(diff.Ticks - timeout.Ticks > 0) {
throw new TimeoutException("session timed out");
}
//store logic
lastAccessedTime = DateTime.Now;
}
public void Commit() {
TimeSpan diff = DateTime.Now - lastAccessedTime;
if(diff.Ticks - timeout.Ticks > 0) {
throw new TimeoutException("session timed out");
}
//commit logic
lastAccessedTime = DateTime.Now;
}}
我有两个问题。- 是否有更好的方法来避免在每个方法开始时进行检查?显然,我可以从代码中重构一个IsAlive属性,但尽管如此,我仍然需要在每个方法的开始检查IsAlive。在。net中有任何Session类,我可以继承我的类吗? 假设,让我说session类中的一个方法返回另一个对象。从超时的角度来看,对该对象的任何调用都需要被视为更新最后访问时间。如果有多个嵌套的对象,这种情况很快就会变得相当复杂。但这只是一个假设性的问题,也许YAGNI;但我想知道是否有什么方法可以完成。
编辑:(因为这些点在原来的问题中没有提到)
- Session类确实有一个回滚方法。我只是没有添加它,以保持代码小!
- store实际上是在数据库事务范围内的后端数据库。因此,在这个类中所有的Store调用实际上会将数据写入数据库,但是在数据库上的commit需要在会话上的commit被调用之后才发生。
在做任何其他事情之前,您应该考虑重新考虑您的会话对象应该负责什么。通常,当我看到Commit
方法时,我也会寻找Rollback
方法,以确保如果操作失败,您的更新在某种程度上是一致的(尽管我仍然不确定类应该做什么)。
此外,如果Commit
提交了由StoreSomething
方法添加的瞬态数据,那么我不明白为什么要打开"会话"(再次,无论那是什么),直到您决定实际提交。
从长远来看,为问题添加更好的描述和一些背景可能会让我们提供更好的解决方案。
话虽如此,一个稍微重构的版本可能是:
-
首先定义一个接口(Liskov替换原则):
public interface IMySession { void StoreName(string name); void StoreAddress(string address); void Commit(); }
-
实现一个具有基本功能的最简单的"普通旧"会话(单一责任原则):
public class BasicSession : IMySession { #region IMySession members public void StoreName(string name) { // plain store } public void StoreAddress(string address) { // plain store } public void Commit() { // plain commit } #endregion }
-
最后,创建一个代理类,它检查超时,并将方法调用转发给基类:
public class TimeLimitedSessionProxy : IMySession { private readonly IMySession _baseSession; private readonly TimeSpan _timeout; private DateTime _lastAccessedTime = DateTime.Now; public TimeLimitedSessionProxy(IMySession baseSession, TimeSpan timeout) { _baseSession = baseSession; _timeout = timeout; } #region IMySession members public void StoreName(string name) { IfNotTimedOut(() => _baseSession.StoreName(name)); } public void StoreAddress(string address) { IfNotTimedOut(() => _baseSession.StoreAddress(address)); } public void Commit() { IfNotTimedOut(() => _baseSession.Commit()); } #endregion private void IfNotTimedOut(Action action) { if (DateTime.Now - _lastAccessedTime > _timeout) { throw new TimeoutException("session timed out"); } action(); _lastAccessedTime = DateTime.Now; } }
代码的其他部分应该接受
IMySession
对象,而不管它在底层是如何实现的。即使
TimeLimitedSessionProxy
也接受IMySession
,对实际实现一无所知;如果您决定添加其他功能,请考虑保留这些类的完整性,并根据需要代理或修饰它们。