设计应用类 [英] designing application classes

查看:23
本文介绍了设计应用类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

除了使用单一职责原则之外,在为正在编写的应用程序设计类时,还应该记住什么,以保持代码的可维护性、可重用性并遵守 OOP 原则?

Besides using the Single Responsibility Principle, when designing classes for an application one is writing, what should one keep in mind, to keep the code maintainable, reusable and adhere to OOP principles?

我发现很难设计我正在尝试编写的应用程序类,因为什么时候决定什么(功能)放在哪个类中,以及它是否真的应该在派生类中,或者应该有一个这个类的抽象类或接口?

I'm finding it hard to design the classes of applications I'm trying to write, because when does one decide what (functionality) goes in which class and whether it should really be in a derived class or there should be an abstract class or interface for this class?

我知道这可能是一个有很多答案的话题,但是有没有人有任何好的指导方针(最好是简单的)来设计易于维护且在创建大型应用程序时不会造成混乱的类和类层次结构?

I know this is probably a topic with many answers, but does anyone have any good guidelines (preferably simple) to designing classes and class hierarchies that are simple to maintain and don't make a mess when creating big applications?

当类有 10 个以上的方法并且有一个抽象基类时它派生自的接口.还有 3 个在类中全局引用的 Singleton 类等等.听起来它需要应用一些重构"?

When there's classes that have 10 methods and over and have an abstract base class & interfaces which it derives from. Also has 3 Singleton classes referenced globally inside the class and much more. Sounds like it needs a bit of 'refactoring' applied?

对不起,如果这是一个很长的例子,但你看到了我面临的问题,我想要一些关于它的输入.请只看设计,而不是技术细节.

Sorry if it's a long example, but you see the problem I'm facing and I want some input on it. Please just look at design, not at technicalities.

我举个例子:

我创建了这个类:(不久前)

I created this class: (a while back)

  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
    }

推荐答案

听说我发布了一些我从我最喜欢的书Architecting Microsoft® .NE​​T Solutions for the Enterprise"中摘录的句子,我强烈推荐阅读这本书即使您不是软件架构师.

Hear I post some of sentence that I did take from my favorite book "Architecting Microsoft® .NET Solutions for the Enterprise" that I strongly recommend to read this book even if you’re not a Software Architect.

视情况而定这总是取决于.作为建筑师,您永远无法确定任何事情.总有可能你会遗漏一些东西.但是,该角色需要做出决定,因此您必须能够评估所有选项并做出明智的决定,并在需要做出决定时迅速做出决定.为了给自己争取一些时间并在后台激活你的心理过程,首先说,这取决于",然后解释为什么以及答案取决于什么.如果您不确定一个点取决于什么,默认答案是这取决于上下文."

It Depends It always depends. As an architect, you are never sure about anything. There's always the possibility that you're missing something. However, the role requires that decisions be made, so you must be able to evaluate all options and make an informed decision, and to do this promptly, when a decision is required. To buy yourself some time and activate your mental processes in the background, first say, "It depends," and then explain why and what the answer depends on. If you are unsure about what a point depends on, the default answer is, "It depends on the context."

要求高于一切架构师只是软件项目中参与者自然链中的一个环节.客户说他想要什么.如果客户不知道他想要什么,就会有人在那里提示他提供具体信息.分析师正式确定客户想要什么.项目经理为正式定义的项目准备基础.架构师获取需求包并将其分类.开发人员跟随架构师.数据库管理员尽最大努力使数据库有效地支持应用程序.注意客户引领链条,客户要的是法律.客户想要的是需求的名称.当然,只有少数客户知道他们想要什么.所以需求发生了变化.

Requirements Are Lord Over All The architect is just one link in the natural chain of actors in a software project. The customer says what he wants. If the customer doesn't know what he wants, someone will be there to prompt him for specifics. The analyst formalizes what the customer wants. The project manager prepares the groundwork for the formally-defined project. The architect gets the bundle of requirements and sorts them out. Developers follow the architect. The database administrator does his best to make the database support the application effectively. Note that the customer leads the chain, and what the customer wants is the law. What the customer wants goes under the name of requirements. Of course, only few customers know what it is they want. So requirements change.

编程接口即使您靠已实现的代码谋生,也应该尽可能利用接口.对我们重复一遍:没有接口就不可能实现."环顾四周,总有一个可以提取的界面.

Program to an Interface Even if you make a living out of implemented code, you should leverage interfaces wherever possible. Repeat with us: "No implementation is possible without an interface." Look around, there's always an interface that can be extracted.

保持简单但不简单你知道 KISS(保持简单,愚蠢),对吧?这只是我们的定制版本.简单和简洁通常相当于伟大和做得好.以简单为目标,但给自己一个范围低端的界限.如果您低于该下限,您的解决方案将变得简单.这不是一件好事.

Keep It Simple but Not Simplistic You know KISS (Keep It Simple, Stupid), right? This is just our customized version. Simple and concise is usually equivalent to great and well done. Aim at simplicity, but give yourself a boundary for the low end of the range. If you go below that lower boundary, your solution will become simplistic. And this is not a good thing.

继承是关于多态性,而不是重用面向对象编程(OOP)告诉我们,我们应该编写一个类,然后永远重用它并随意扩展它.这要归功于继承.这自然会扩展到类重用吗?重用是一个比您最初想象的要微妙得多的概念.多态性是 OOP 要利用的关键方面.多态意味着您可以互换使用两个继承的类.正如其他人所说,重用是一个很好的副作用."但是重用不应该是您的目标,或者换句话说,不要仅仅为了重用类而通过继承重用类.最好编写一个更精确地满足需求的新类,而不是尝试继承一个不是为工作而设计的现有类.

Inheritance Is About Polymorphism, Not Reuse Object-oriented programming (OOP) taught us that we should write a class once and reuse it forever and extend it at will. And this is possible thanks to inheritance. Does this naturally extend to class reuse? Reuse is a much subtler concept than you might think at first. Polymorphism is the key aspect of OOP to leverage. Polymorphism means you can use two inherited classes interchangeably. As others have said, "Reuse is a nice side effect to have." But reuse shouldn't be your goal, or put another way, don't reuse a class through inheritance just to reuse the class. It's better to write a new class that more precisely fits the needs than to try to inherit an existing class that wasn't designed for the job.

不是 DAL?那就别碰 SQL与我们一起重复:关注点分离.关注点分离."将数据访问代码和详细信息(例如连接字符串、命令和表名称)推到角落.迟早,您需要处理它们,但是将业务和表示逻辑与持久性分开考虑.如果可能,将持久性委托给临时工具,例如对象/关系映射器 (O/RM) 工具.

Not the DAL? Don't Touch SQL Then Repeat with us: "Separation of concerns. Separation of concerns." Push data access code and details (such as connection strings, commands, and table names) to the corner. Sooner or later, you need to take care of them, but consider business and presentation logic separately from persistence. And if possible, delegate persistence to ad hoc tools such as Object/ Relational Mapper (O/RM) tools.

可维护性第一如果你只能为你的软件选择一个属性,它会是什么?可扩展性?安全?表现?可测试性?可用性?对我们来说,以上都不是.对我们来说,首先是可维护性.通过可维护性,您可以随时实现任何其他目标.

Maintainability First If you could pick only one attribute for your software, what would it be? Scalability? Security? Performance? Testability? Usability? For us, it would be none of the above. For us, what comes first is maintainability. Through maintainability, you can achieve anything else at any time.

所有用户输入都是邪恶的你应该已经听说过这个了.如果有办法让用户做错事,他们会找到的.哦,这听起来像墨菲定律.是的,你也应该听说过这个.

All User Input Is Evil You should have heard this already. If there's a way for users to do something wrong, they'll find it. Oh, this sounds like Murphy's Law. Yes, you should have heard this one, too, already.

事后优化Donald Knuth 说过早优化是所有软件邪恶的根源.我们走得更远.不要优化系统.相反,将其设计为随时改进和扩展.但是只有在系统被解散时才专注于纯优化.

Post-Mortem Optimization Donald Knuth said that premature optimization is the root of all software evil. We go even further. Do not optimize the system. Instead, design it for being improved and extended at any time. But focus on pure optimization only when the system is dismissed.

安全性和可测试性是设计的如果您认真对待某个系统属性,请从一开始就为其设计.安全性和可测试性也不例外,国际标准化组织 (ISO) 标准明确指出了这一点.

Security and Testability Are by Design If you're serious about a system attribute, design for it right from the beginning. Security and testability are no exception to this rule, and there's an International Organization for Standardization (ISO) standard that specifically says so.

希望这对你有帮助.

这篇关于设计应用类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆