【问题标题】:Design pattern to ensure that a method A is called before method B确保在方法 B 之前调用方法 A 的设计模式
【发布时间】:2013-08-28 23:09:56
【问题描述】:

我有一个像

这样的示例(不完整)类
class ABC{   
public:      
    void login();      
    void query_users();       
    //other methods  
private:      
    //member data
}

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

【问题讨论】:

  • 我建议检查一些数据成员是否已设置是安全的做法。
  • 我建议使用单个状态变量
  • 当然,另一种方式是拥有两个对象。创建 ABC 对象的那个也调用 login 方法。这个外部对象是唯一可以提供 ABC 对象的对象。
  • 我正在写一些关于可能使用注释的内容,然后我阅读了 Mats Petersson 的评论 - 我喜欢这种方法。基本上使用建造者模式或工厂。

标签: oop design-patterns


【解决方案1】:

我知道有两种通用方法,它们有很大的不同。您必须为任务选择适当的机制 - 在标准的基于类的 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 类型(方法 Logon)创建 ServiceAccess(方法 DoStuff)。因此,DoStuff 只能在从Logon(在 ServiceLogon 上)创建后被调用(在 ServiceAccess 类型上)。这可以很好地在具有成员隐藏的静态语言中强制执行调用顺序语义 - 因为如果错误,程序将无法编译。

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

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

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

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

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

【讨论】:

  • 我必须同意答案#2,如果你使用这个过程,编译器实际上可以在运行之前捕捉到这个问题,因为它不可能以错误的顺序完成。
【解决方案2】:

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

abc = ABC.login(); //sets all the state
users = abc.query_users();

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      喜欢:

      public void login(){
        //do login code
          if(//code for login check){
               //run other methods
          }
          else{
              login(); //re-run login workings
          }
      }
      

      【讨论】:

      • 这是一个危险的登录功能骨架:p
      • 如果你设置一个 if 来检查它只完成了 ~3 次迭代或其他什么,然后取消它应该递归地完成工作:P
      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 2013-06-12
        • 2020-05-16
        • 2021-09-02
        • 1970-01-01
        • 1970-01-01
        • 2021-08-05
        • 2019-04-04
        • 1970-01-01
        • 2022-01-19
        相关资源
        最近更新 更多