正文
功能要求
实现登录功能,能够储存多个账户密码。有三种不同的登陆模式,可以分别生成对应的题目并且输出到文档之中。出题有查重功能,同一个人出的题不能重复。
功能实现
这位同学使用了一个状态模式去实现几种不同的登录状态(以及正在登录的状态),由一个指令接受类在命令行接受命令,在这个环境经过内建命令的处理,然后发送给当前状态之中。
登录、输出两个模块是独立的。各自独立的几个模块通过发布订阅模式来联系。他实现了一个事件中心,实现了不同的类均可以通过事件中心单例去订阅和发布事件。
其中,命令接受、事件中心、输出以及登录几个模块是单例的。
具体实现
事件中心
namespace EventSystem
{
// 标准的回调函数委托接口
public interface IEventCallBack
{ }
// 回调函数事件的两个重载
public class EventCallBack : IEventCallBack
{
public Action actions;
public EventCallBack(Action action)
{
actions += action;
}
}
public class EventCallBack<T> : IEventCallBack
{
public Action<T> actions;
public EventCallBack(Action<T> action)
{
actions += action;
}
}
// 本项目的事件中心,实现完全解耦合的发布订阅模式
public class EventCenter
{
// 单例
private static EventCenter? instance = null;
// 事件管理字典
private Dictionary<EventType, IEventCallBack> _events =
new Dictionary<EventType, IEventCallBack>();
private EventCenter()
{
// do nothing
}
public static EventCenter GetInstance()
{
if(instance==null)
{
instance = new EventCenter();
}
return instance;
}
/// <summary>
/// 订阅事件
/// </summary>
/// <param name="type">事件类型</param>
/// <param name="action">以委托形式传递的回调函数</param>
public void EventSubscribe(EventType type, Action action)
{
if(_events.ContainsKey(type))
{
(_events[type] as EventCallBack).actions += action;
}
else
{
_events.Add(type, new EventCallBack(action));
}
}
public void EventSubscribe<T>(EventType type, Action<T> action)
{
if(_events.ContainsKey(type))
{
(_events[type] as EventCallBack<T>).actions += action;
}
else
{
_events.Add(type, new EventCallBack<T>(action));
}
}
/// <summary>
/// 退订事件
/// </summary>
/// <param name="type">事件类型</param>
/// <param name="action">以委托形式传递的回调函数</param>
public void EventUnsubscribe(EventType type, Action action)
{
if (_events.ContainsKey(type))
{
(_events[type] as EventCallBack).actions -= action;
}
}
public void EventUnsubscribe<T>(EventType type, Action<T> action)
{
if(_events.ContainsKey(type))
{
(_events[type] as EventCallBack<T>).actions -= action;
}
}
/// <summary>
/// 发布事件
/// </summary>
/// <param name="type">事件类型</param>
public void EventTrigger(EventType type)
{
if (_events.ContainsKey(type))
{
(_events[type] as EventCallBack).actions?.Invoke();
}
}
/// <summary>
/// 发布事件
/// </summary>
/// <param name="type">事件类型</param>
/// <param name="args">传递参数</param>
public void EventTrigger<T>(EventType type, T args)
{
if(_events.ContainsKey(type))
{
(_events[type] as EventCallBack<T>).actions?.Invoke(args);
}
}
/// <summary>
/// 清空事件列表
/// </summary>
public void Clear()
{
_events.Clear();
}
}
}
状态管理
public class StateManager
{
// 当前生效的状态
private IState _state ;
// 当前登录的用户名
public string Username;
// 该用户的出题历史
public HashSet<string> UserTestHistory = new HashSet<string>();
// 该用户的出题总数
private int _count=0;
// 可能存在的四种状态
private LoginState _loginState = new LoginState();
private PrimaryState _primaryState = new PrimaryState();
private JuniorState _juniorState = new JuniorState();
private SeniorState _seniorState = new SeniorState();
// 构造函数
public StateManager()
{
// 初始状态为待登录状态
_state = _loginState;
_state.Handle(this);
EventCenter.GetInstance().EventSubscribe<Account>(
EventType.LoginSuccess,
(account) => LoginSuccess(account)
);
}
// 切换当前状态
public void StateSwitch(Grade type)
{
switch(type)
{
case Grade.None:
_state = _loginState;
break;
case Grade.Primary:
_state = _primaryState;
break;
case Grade.Junior:
_state = _juniorState;
break;
case Grade.Senior:
_state = _seniorState;
break;
default:
break;
}
_state.Handle(this);
}
/// <summary>
/// 主要操作类接收指令的入口
/// </summary>
/// <param name = "command">命令行读取入的指令</param>
public void CommandRecv(string command)
{
_state.CommandRecv(command);
}
// 登录成功后的处理
public void LoginSuccess(Account account){
Username = account.UserName;
// 检查是否已经存在对应用户文件并进行创建
if( Directory.Exists(@"./File/"+Username) == true)
{
// 存在用户文件夹
if(!File.Exists(@"./File/"+Username+@"/"+Username+".txt"))
{
FileStream fs = new FileStream(@"./File/"+Username+@"/"+Username+".txt", FileMode.Append);
StreamWriter wr = null;
wr = new StreamWriter(fs);
wr.WriteLine("0");
wr.Close();
}
}
else
{
// 创建一个记录为0条的用户出题历史
Directory.CreateDirectory(@"./File/"+Username);
FileStream fs = new FileStream(@"./File/"+Username+@"/"+Username+".txt", FileMode.Append);
StreamWriter wr = null;
wr = new StreamWriter(fs);
wr.WriteLine("0");
wr.Close();
}
// 读取用户出题数据
StreamReader rd = File.OpenText(@"./File/"+Username+@"/"+Username+".txt");
string s = rd.ReadLine();
// 出题历史的总条数
_count = int.Parse(s);
for (int i = 0; i < _count; i++) //读入数据并赋予数组
{
string line = rd.ReadLine();
UserTestHistory.Add(line);
}
rd.Close();
}
// 保存出题历史
public void SaveHistory()
{
System.IO.File.WriteAllText(@"./File/"+Username+@"/"+Username+".txt", string.Empty);
FileStream fs = new FileStream(@"./File/"+Username+@"/"+Username+".txt",FileMode.Append);
StreamWriter wr = null;
wr = new StreamWriter(fs);
wr.WriteLine(_count);
foreach(string test in UserTestHistory)
{
wr.WriteLine(test);
}
wr.Close();
}
/// <summary>
/// 检查试题是否重复
/// </summary>
public bool AddTestHistory(string test)
{
string code = MD5.GetInstance().StringToMD5_UTF8(test);
if(UserTestHistory.Contains(code))
{
return false;
}
_count++;
UserTestHistory.Add(code);
return true;
}
}
运行效果
优点
系统的结构有设计,易于拓展,条例相对清晰,不同模块之间耦合度低(发布订阅模式),便于后期代码维护和拓展。
注释非常全面,许多跨类调用的函数都有精确到每个参数的注释,便于代码阅读。
命名比较符合c#的命名规则(参考网络上的谷歌开源c#代码规范),阅读起来比较方便。
有基于安全性考虑而采用的哈希算法加密。
缺点
代码过多,有一些功能稍显无用。
单例模式过多,许多独立的模块都是单例,其中一些是可以改善的。
资源相对开销较大。
对于结构体和接口的使用,一部分仍存在一些问题,需要重构优化一下。
有的注释比较繁琐,不够精炼,应当注意注释的简洁明了。
没有使用数据库,而是在本地用字典保存用户信息,这一部分值得拓展一下。
虽然考虑到了安全性以及系统的结构,但是没有充分考虑到应用过程中的数据传输结构,数据打包方式等等。在真正把它做成实用的系统的时候,可能还需要大规模的重构。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/289298.html