3

我有一个示例(不完整)类,例如

class ABC{   
public:      
    void login();      
    void query_users();       
    //other methods  
private:      
    //member data
}

这个类的使用方式应该是先调用login,然后才能调用其他方法,如query_users等。登录设置一些私有成员数据以供其他方法使用。除了调用一个函数来检查成员数据是否设置在类中每个其他方法的开头,还有什么更简单的方法可以实现这一点?

4

5 回答 5

5

我知道有两种通用方法,它们有很大的不同。您必须为任务选择适当的机制——在标准的基于类的 OO 语言(例如 Java/C++/C#/Python)中,它们是我所知道的仅有的两种方法。(我不熟悉的不同范式中可能还有其他方法。)

1.检查状态。

这已经在许多必须跟踪系统/支持资源状态的类中完成。两个常见的例子是(文件)流和数据库连接。

“模板”可能如下所示:

void Logon(credentials) { ..; loggedOn = true }
void DieUnlessLoggedIn { if (!loggedOn) { throw .. } }
void DoStuff () { DieUnlessLoggedIn(); .. }

虽然上述方法非常通用,但某些语言可能支持不变量(Eiffel)、装饰(Python)、注释、AOP 或其他断言机制。

这种方法对于可变世界中的动态状态很有用:例如“注销”之后会发生什么?状态DoStuff再次无效,直到重新登录(如果允许)。但是,这种方法通常不能用于主流 OOP 语言中的编译时检查,因为运行时状态在编译时根本不可用。

2. 使用多种类型来表示状态。

创建两个单独的类型,例如 ServiceLogon (method Logon) 类型创建 ServiceAccess (method DoStuff)。因此DoStuff只能在从Logon(在 ServiceLogon 上)创建之后调用(在 ServiceAccess 类型上)。这可以很好地在具有成员隐藏的静态语言中强制执行调用顺序语义 - 因为如果错误,程序将无法编译。

login = new ServiceLogon(credentials)
access = login.Logon();
access.DoStuff();        // can't be called before obtained via Logon

使用类型编码附加状态可能过于复杂,因为它会破坏基于类的类型系统,但在“构建器”和“存储库”模式等中很有用;基本上,询问类型是否需要拆分以维护 SRP,然后考虑这种方法

如果不结合状态检查,这种方法无法完全处理“注销”之类的事情,因为类型 ServiceAccess(在干净的意义上)总是表示相同的状态,因为它被编码在类型中。

1. & 2. 使用状态检查状态/角色特定类型。

当然,混合是完全可以接受的,上述两种方法并不相互排斥。分离使一种类型(以及由此调用的方法)依赖于另一种方法的角色可能是有意义的,同时仍适当地检查运行时状态。如上所述,#1 非常适合运行时守卫(可以是高度动态的),而#2 可以在编译时强制执行某些规则。

于 2013-08-28T23:22:35.333 回答
1

您可以做的是从返回您可以使用的实例的静态工厂方法中创建 ABC 的实例。在伪代码中:

abc = ABC.login(); //sets all the state
users = abc.query_users();
于 2013-08-29T01:10:34.200 回答
0

我不确定这是最好的方法,但是您可以将login()其设为私有并将其作为构造函数的一部分调用,这将确保login()在对象创建本身时调用它,然后只能调用任何其他函数(除非您有静态功能)

class ABC{   
public ABC(credentials){
    login(credentails);
}
public:           
    void query_users();       
    //other methods  
private:      
    void login(); 
    //member data
}
于 2013-08-28T23:13:05.020 回答
0

当它从上到下时,它已经首先工作了。如果要确保登录成功,请从 login() 方法内部调用其他方法。

喜欢:

public void login(){
  //do login code
    if(//code for login check){
         //run other methods
    }
    else{
        login(); //re-run login workings
    }
}
于 2013-08-28T23:14:48.670 回答
0

如果你真的想遵循好的模式,你可以尝试让尽可能多的类不可变。

这意味着您的构造函数设置了总状态(完成整个登录),然后方法调用的顺序完全不相关。

于 2013-08-29T01:27:26.570 回答