Ton*_*ion 2 c# architecture design-patterns
除了使用单一责任原则之外,在为应用程序设计类时,人们正在编写,应该记住什么,以保持代码可维护,可重用并遵守OOP原则?
我发现很难设计我正在尝试编写的应用程序类,因为什么时候决定哪个(功能)在哪个类中进行,以及它是否应该真正在派生类中,或者应该有一个抽象类或这个类的接口?
我知道这可能是一个有许多答案的主题,但是有没有人有任何好的指导方针(最好是简单的)来设计易于维护的类和类层次结构,并且在创建大型应用程序时不会弄得一团糟?
编辑:
当有超过10个方法的类时,它们有一个抽象的基类和接口.还有3个在类中全局引用的Singleton类以及更多.听起来需要应用一些"重构"?
对不起,如果这是一个很长的例子,但你看到我面临的问题,我想要一些输入.请看一下设计,而不是技术问题.
我举个例子:
我创建了这个类:(前一阵子)
class ExistingUserLogon : Logon, ILogonUser
{
#region Member Variables
LogonEventArgs _logoneventargs;
LogonData lgndata;
Factory f = Factory.FactoryInstance;
PasswordEncrypt.Collections.AppLoginDataCollection applogindatacollection;
PasswordEncrypt.Collections.SQlLoginDataCollection sqllogindatacollection;
bool? compare;
static ExistingUserLogon existinguserlogon;
PasswordEncrypt.SQLDatabase.DatabaseLogin dblogin;
string databasename = string.Empty;
#endregion
#region Properties
public static ExistingUserLogon ExistingUserLogonInstance
{
get
{
if (existinguserlogon == null)
existinguserlogon = new ExistingUserLogon();
return existinguserlogon;
}
}
public string loginname
{
get;
set;
}
#endregion
#region Contructors
public ExistingUserLogon(bool? compare, LogonData logondata)
{
this.compare = compare;
this.lgndata = logondata;
this.applogindatacollection = f.AppLoginDataCollection;
this.sqllogindatacollection = f.SqlLoginDataCollection;
}
public ExistingUserLogon()
{
this.applogindatacollection = f.AppLoginDataCollection;
this.sqllogindatacollection = f.SqlLoginDataCollection;
}
#endregion
#region Delegates
public delegate void ConnStrCreated( object sender, LogonEventArgs e );
#endregion
#region Events
public event ConnStrCreated ConnectionStringCreated;
#endregion
private void OnConnectionStringCreated( object sender, LogonEventArgs e )
{
if (ConnectionStringCreated != null)
{
ConnectionStringCreated(sender, e);
}
}
public void LoginNewUser()
{
dblogin = new PasswordEncrypt.SQLDatabase.DatabaseLogin();
if (!string.IsNullOrEmpty(loginname))
{
string temp = dblogin.GenerateDBUserName(loginname);
if (temp != "Already Exists")
{
if (compare == true)
{
IterateCollection(lgndata.HahsedUserName.HashedUserName, new Action<string>(OnDatabaseName));
IterateCollection(temp, new Action<bool, string, string>(OnIterateSuccess));
}
}
}
else
{
LogonEventArgs e = new LogonEventArgs();
e.Success = false;
e.ErrorMessage = "Error! No Username";
OnError(this, e);
}
}
private void OnDatabaseName(string name)
{
this.databasename = name;
}
private void OnIterateSuccess( bool succeed, string psw, string userid )
{
if (succeed)
{
// Create connectionstring
ConnectionStringCreator cnstrCreate = ConnectionStringCreator.ConnectionStringInstance;
if (databasename != string.Empty)
{
string conn = ConnectionStringCreator.CreateConnString(databasename, userid, psw);
bool databaseExists;
databaseExists = DataManagementVerification.DoDatabaseExists(conn);
if (databaseExists)
{
FormsTransfer.ConnectionString = conn;
conn = string.Empty;
}
else
{
LogonEventArgs e = new LogonEventArgs();
e.Success = false;
e.ErrorMessage = "Database does not Exist!";
OnError(this, e);
}
//OnConnectionStringCreated(this, e);
// PasswordEncrypt.LINQtoSQL.PasswordDatabase db = new PasswordEncrypt.LINQtoSQL.PasswordDatabase(conn);
/* PasswordEncrypt.LINQtoSQL.EncryptData _encryptdata = new PasswordEncrypt.LINQtoSQL.EncryptData()
{
EncryptID = 1,
IV = "Test",
PrivateKey = "Hello",
PublicKey = "Tony"
};
db.EncryptionData.InsertOnSubmit(_encryptdata);
// db.SubmitChanges();*/
//PasswordEncrypt.LINQtoSQL.Data _data = new PasswordEncrypt.LINQtoSQL.Data()
//{
// EncryptionID = 1,
// Username = "Tbone",
// Password = "worstje",
// IDCol = 2
//};
//db.Data.InsertOnSubmit(_data);
//db.SubmitChanges();
//IEnumerable<PasswordEncrypt.LINQtoSQL.Data> _ddata = db.Data.Where(data => data.Password == "worstje"); ;
//foreach (PasswordEncrypt.LINQtoSQL.Data data in _ddata)
//{
// MessageBox.Show("Data Found: " + data.Username + "," + data.Password);
//}
}
else
{
MessageBox.Show("Found no Database name", "Database name error");
}
}
else
{
// Unable to create connectionstring
}
}
private void IterateCollection( string username, Action<bool, string, string> OnSucceed )
{
bool succeed = false;
dblogin = new PasswordEncrypt.SQLDatabase.DatabaseLogin();
string psw;
string userid;
foreach (KeyValuePair<PasswordEncrypt.Collections.GItem<string>, PasswordEncrypt.Collections.GItem<string>> kv in sqllogindatacollection)
{
List<char> tempa = new List<char>();
List<char> tempb = new List<char>();
tempa = dblogin.GetNumber(username);
tempb = dblogin.GetNumber(kv.Key.Item);
if (tempa.Count == tempb.Count)
{
for (int i = 0; i < tempa.Count ; i++)
{
if (tempa.ElementAt(i).Equals(tempb.ElementAt(i)))
{
if ( i == (tempa.Count -1) )
succeed = true;
continue;
}
else
{
KeyValuePair<PasswordEncrypt.Collections.GItem<string>, PasswordEncrypt.Collections.GItem<string>> last = sqllogindatacollection.Last();
if (kv.Key.Item.Equals(last.Key.Item))
{
MessageBox.Show("Failed to match usernames for Database", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
succeed = false;
// Let GUI Know...
LogonEventArgs e = new LogonEventArgs();
e.Success = succeed;
OnError(this, e);
break;
}
else
{
break;
}
}
}
// MessageBox.Show("Found a sql username match");
}
// Now go execute method to logon into database...
if (succeed)
{
psw = kv.Value.Item;
userid = kv.Key.Item;
OnSucceed(succeed, psw, userid);
}
succeed = false;
}
}
private void IterateCollection(string key, Action<string> OnDatabaseName )
{
foreach (KeyValuePair<PasswordEncrypt.Collections.GItem<string>, PasswordEncrypt.Collections.GItem<string>> kv in applogindatacollection)
{
if (key == kv.Key.Item)
{
MessageBox.Show("Found a match");
OnDatabaseName(kv.Value.Item);
}
else
{
// MessageBox.Show("No Match");
}
}
}
#region Public Methods
public bool? ReadFromRegistry( HashedUsername username, HashedPassword hashedpassword )
{
return RegistryEdit.ReadFromRegistry(username, hashedpassword);
}
public bool WriteToRegistry( HashedUsername username, HashedPassword hashedpassword )
{
return RegistryEdit.WriteToRegistry(username, hashedpassword);
}
public override void Login(TextBox username, TextBox password)
{
base.Login(username, password);
Login(username.Text, password.Text);
}
protected override void Login(string username, string password)
{
base.Login(username, password);
lgndata = base._logondata;
compare = base._regRead;
if (compare == true)
{
loginname = username;
LoginNewUser();
/// on login succeeded let UI class know
_logoneventargs = new LogonEventArgs();
_logoneventargs.Success = true;
OnExistingUserLoggedIn(this, _logoneventargs);
}
else
{
_logoneventargs = new LogonEventArgs();
_logoneventargs.Success = false;
_logoneventargs.ErrorMessage = "Cannot Login this user, please try again.";
OnError(this, _logoneventargs);
}
/// Get username and password for database...
/// to login using correct user data & permissions
/// Login data for database is generated at runtime
/// then by checking if database with such a name exists
/// login...
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
听听我从我最喜欢的书"为企业构建Microsoft®.NET解决方案"中发表的一些句子,即使您不是软件架构师,我强烈建议您阅读本书.
它取决于 它总是取决于.作为一名建筑师,你永远不会确定任何事情.总有可能你错过了什么.但是,角色需要做出决策,因此您必须能够评估所有选项并做出明智的决定,并在需要做出决定时迅速做出决定.为了给自己买一些时间并在后台激活你的心理过程,先说"这取决于",然后解释为什么以及答案取决于什么.如果您不确定某个点取决于什么,默认答案是"这取决于上下文".
要求是主宰 建筑师只是软件项目中自然链中的一个环节.顾客说出他想要的东西.如果顾客不知道他想要什么,有人会在那里提示他具体细节.分析师正式确定了客户的需求.项目经理为正式定义的项目准备基础.架构师获得了一系列需求并对其进行排序.开发人员遵循架构师.数据库管理员尽最大努力使数据库有效地支持应用程序.请注意,客户领导链条,客户想要的是法律.客户想要的是以要求为名.当然,只有少数客户知道他们想要什么.因此需求发生变化
编程到接口 即使您使用已实现的代码谋生,也应尽可能利用接口.与我们重复:"没有接口就无法实现." 环顾四周,总有一个可以提取的界面.
保持简单但不简单 你知道KISS(保持简单,愚蠢),对吧?这只是我们的定制版本.简单而简洁通常相当于伟大而且做得好.瞄准简单,但给自己一个低端范围的边界.如果你低于那个较低的边界,你的解决方案将变得简单化.这不是一件好事.
继承是关于多态的,而不是重用 面向对象的编程(OOP)告诉我们,我们应该编写一次类并永久地重用它并随意扩展它.这可以归功于继承.这自然会扩展到类重用吗?重用是一个比你最初想象的更微妙的概念.多态性是OOP利用的关键方面.多态性意味着您可以交替使用两个继承的类.正如其他人所说,"重用是一个很好的副作用." 但重用不应该是你的目标,或换句话说,不要通过继承重用一个类来重用该类.编写一个更符合需求的新类比尝试继承不是为作业设计的现有类更好.
不是DAL?不要触摸SQL然后 重复我们:"关注点分离.关注点分离." 将数据访问代码和详细信息(例如连接字符串,命令和表名称)推送到角落.迟早,您需要处理它们,但将业务和表示逻辑与持久性分开考虑.如果可能,将持久性委托给对象/关系映射器(O/RM)工具等临时工具.
可维护性首先 如果您只为您的软件选择一个属性,它会是什么?可扩展性?安全?性能?可测试性?可用性?对我们来说,它不会是上述情况.对我们来说,首先是可维护性.通过可维护性,您可以随时实现其他任何目标.
所有用户输入都是邪恶 你应该已经听过这个.如果用户有办法做错事,他们会找到它.哦,这听起来像墨菲定律.是的,你也应该已经听过这个了.
事后优化 Donald Knuth说,过早优化是所有软件邪恶的根源.我们走得更远.不要优化系统.相反,设计它可以随时进行改进和扩展.但只有在系统被解雇时才专注于纯粹的优化.
安全性和可测试性是设计的 如果您对系统属性非常认真,请从一开始就对其进行设计.安全性和可测试性也不例外,并且有一个国际标准化组织(ISO)标准,具体说明了这一点.
我希望你能帮助你.