除了使用“单一职责原则”之外,在为应用程序设计类时,还要编写一个类,以使代码可维护,可重用并遵守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
}
最佳答案
听到我发表的一句话,摘自我最喜欢的书“为企业构建Microsoft®.NET解决方案”,即使您不是软件架构师,我也强烈建议您阅读这本书。
这取决于
它总是取决于。作为建筑师,您永远不会有任何疑问。总是有可能您遗漏了一些东西。但是,该角色要求您做出决定,因此您必须能够评估所有选项并做出明智的决定,并在需要作出决定时迅速做到这一点。要花一些时间在后台激活自己的思维过程,请先说“这取决于”,然后解释答案的原因和原因。如果不确定某个点所依赖的内容,则默认答案为“它取决于上下文”。
需求至上
架构师只是软件项目中行为者自然链中的一个环节。客户说出他想要的。如果客户不知道他想要什么,那么有人会在那里提示他输入详细信息。分析师将客户的需求形式化。项目经理为正式定义的项目准备基础。架构师获取大量需求并将其分类。开发人员跟随建筑师。数据库管理员将尽最大努力使数据库有效地支持应用程序。请注意,客户领导链条,客户想要的是法律。客户想要的是需求的名称。当然,只有很少的客户知道他们想要什么。因此需求发生了变化。
编程到接口
即使您以实现的代码为生,也应尽可能利用接口。与我们重复:“没有接口就不可能实现。”环顾四周,总是有一个可以提取的接口。
保持简单而不简单
你知道KISS(保持简单,愚蠢),对吗?这只是我们的定制版本。简单明了通常等同于出色完成。力求简单,但要给自己定下界限。如果您低于该下限,您的解决方案将变得简单。这不是一件好事。
继承是关于多态性,而不是重用
面向对象编程(OOP)告诉我们,我们应该编写一次类并永久重用并随意扩展它。由于继承,这是可能的。这自然会扩展到类重用吗?重用是一个比您一开始可能想的要微妙得多的概念。多态是OOP要利用的关键方面。多态意味着您可以互换使用两个继承的类。正如其他人所说,“重用是一个很好的副作用。”但是重用不是您的目标,或者换句话说,不要通过继承重用一个类,而只是重用该类。最好编写一个更适合需要的新类,而不是尝试继承一个并非为该工作而设计的现有类。
不是DAL吗?然后不要接触SQL
与我们重复:“关注点分离。关注点分离。”将数据访问代码和详细信息(例如连接字符串,命令和表名)推到角落。迟早需要照顾它们,但要与持久性分开考虑业务和表示逻辑。并且,如果可能,将持久性委派给特定工具,例如对象/关系映射器(O / RM)工具。
可维护性至上
如果您只能为软件选择一个属性,那将是什么?可扩展性?安全?性能?可测试性?可用性?对于我们来说,以上都不是。对我们来说,最重要的是可维护性。通过可维护性,您可以随时实现其他目标。
所有用户输入都是错误的
您应该已经听到了。如果用户有办法做错事,他们会发现的。哦,这听起来像墨菲定律。是的,您也应该已经听到过这一声音。
事后优化
唐纳德·克努斯(Donald Knuth)说,过早的优化是所有软件弊端的根源。我们走得更远。不要优化系统。相反,可以随时对其进行设计以进行改进和扩展。但是,只有在系统被淘汰时才专注于纯优化。
安全性和可测试性是设计使然
如果您认真对待系统属性,请从一开始就对其进行设计。安全性和可测试性也不是该规则的例外,并且有一个国际标准化组织(ISO)标准专门说明了这一点。
希望对您有帮助。
关于c# - 设计应用程序类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1962175/