【问题标题】:Enforce method execution order in Abstract Class在抽象类中强制执行方法的顺序
【发布时间】:2014-11-25 08:53:05
【问题描述】:

我有一个商业案例,其中需要按顺序发生 3 件事:

  1. 下载()
  2. 进程()
  3. 上传()

现在,抽象类 FileTransfer 提供了 1.downloadFiles() 和 3.upload() 的实现,但不提供 2.process() -- 子类(如 MusicFileTransfer 或 VideoFileTransfer 或 PDFFileTransfer)将做不同的事情在2.process()阶段。

因此,将抽象类设为这样似乎很清楚:

public abstract class FileTransfer {

public void download() {
    // implementation provided
}

public abstract void process(); // implementation not provided

public void upload() {
    // implementation provided
}

}

但有一个问题——永远不会 永远有一段时间可以让 MusicFileTransfer 在下载之前调用 process()( ) 或任何其他命令。进程必须始终是 1.download(),2.process(),然后 3.upload()。

所以我想像这样:

public void doTransfer() {
    // private methods since we want to enforce this order of execution
    download(); // implem provided
    process(); // abstract method
    upload(); // implem provided
}

在 FileTransfer 中环绕这三个调用。 但为了让子类 MusicFileTransfer 覆盖 process() ...它必须是公共的或受保护的(不是私有的)。

我应该怎么做才能解决这个泡菜?有一个公共的 doTransfer() 一个公共的 process() 并确保 process() 永远不会被调用?还是取消 doTransfer() 并希望顺序始终正确?

【问题讨论】:

    标签: java oop design-patterns abstract-class


    【解决方案1】:

    这里的典型方法是跟踪状态,因此如果这些方法之一被乱序调用,那么您将抛出 IllegalStateException - 但是如果您的抽象方法 process 是实现此逻辑的地方,那么当然你不会有这种控制权。

    final void process() {
        if (state != DOWNLOAD) throw new IllegalStateException("Download must be invoked first");
        ...
    

    对此的一种可能解决方案是采用类似于java.lang.Thread 的方法,其中调用者调用start 而实现者覆盖run。然后,您可以将 doProcess 公开为您的抽象方法,当您的调用者调用 process 时调用该方法:

    void process() { // from base-class
        if (state != DOWNLOAD) throw new IllegalStateException("Download must be invoked first");
        doProcess();
    }
    
    final void doProcess() { // implementing abstract method
        // implementation logic here
    

    或者,对于您的客户端代码来说,最简洁的方法可能是使用“委托”:将您的原始基类子类化为维护调用责任的委托者,尽管可能需要您提供更多样板文件你的实现类:

    class FileTransferDelegator extends FileTransfer {
    
        final Filetransfer delegate;
    
        FileTransferDelegator(Filetransfer delegate) {
            this.delegate = delegate;
        }
    
        void process() {
            if (state != DOWNLOAD) throw new IllegalStateException("Download must be invoked first");
            delegate.process();
        }
    

    【讨论】:

      【解决方案2】:

      如果扩展 FileTransfer 的所有类都在同一个包中,则可以使 download()upload()process() 受保护方法。这样你就限制了谁可以打电话给他们。

      然后保持你的模板方法,doTransfer() public,这样它是唯一可以被外部类(包外)调用的方法

      我还要添加一个 javadoc 注释,指定只应调用 doTransfer()

      像这样:

      public abstract class FileTransfer {
      
          /**
           * Handles the file transfer process
           */
          public void doTransfer() {
              // private methods since we want to enforce this order of execution
              download(); // implem provided
              process(); // abstract method
              upload(); // implem provided
          }
      
          /**
           * Invoking outside of doTransfer can cause unexpected behavior.
           */
          // If you never want subclasses to override this, make it private
          protected void download() {
              // implementation provided
          }
      
          /**
           * Invoking outside of doTransfer can cause unexpected behavior.
           */
          // If you never want subclasses to override this, make it private
          public void upload() {
              // implementation provided
          } 
      
          /**
           * Invoking outside of doTransfer can cause unexpected behavior.
           */
          // Only the subclasses in the same package will be able to access this
          // for implementation purposes
          protected abstract void process(); // implementation not provided
      }
      

      使用您的类的其他人的方法的 API 将是

      FileTransfer
          void doTransfer()  
              Handles transfering the files etc...
      

      API 中不应提供任何其他方法。

      注意: 但是,实现process() 的子类仍然可以调用它。没有办法完全阻止他们这样做。您可以做的最好的事情是添加说明不鼓励这样做的文档。

      另一个问题是,因为您不知道子类实际上是如何实现process(),所以您不能肯定地说它不能单独调用。您只是假设他们以您期望的方式实现它,这会导致其行为不正确。

      【讨论】:

      • 非常感谢您的意见。您和 rgettman 已经向我说明了这一点,我非常感谢。
      【解决方案3】:

      在这种情况下,您可能会考虑使 process() 方法受到保护,这意味着子类可以扩展它,但消费者可能不会直接调用它。此外,也许 download() 和 upload() 方法也不应该是公开的。考虑以下几点:

      public abstract class FileTransfer {
      
          private void download() {
              // implementation provided
          }
      
          protected abstract void process(); // implementation not provided
      
          private void upload() {
              // implementation provided
          }
      
          public void doTransfer() {
              // private methods since we want to enforce this order of execution
              download(); // implem provided
              process(); // abstract method
              upload(); // implem provided
          }
      
      }
      

      示例实现类:

      public class FTPFileTransfer {
      
          protected void process() {
              // implement FTP file transfer here
      
          }
      }
      

      示例消费者方法:

      public void consumerMethod() {
          FileTransfer fileTransfer = factory.getFTPFileTransfer();
          fileTransfer.doTransfer();
      } 
      

      【讨论】:

        【解决方案4】:

        您需要template method pattern。在FileTransfer 类中创建一个doTransfer 方法(final),定义需要调用的方法的顺序。使downloadupload 方法也成为final,这样它们就不能被覆盖。 process 方法是abstract 方法;使其成为protected,因此不能从其他类中调用它。不能是public,否则会被其他类乱序调用。

        public final void doTransfer() {
            // protected methods since we want to enforce this order of execution
            download(); // implem provided
            process(); // abstract method
            upload(); // implem provided
        }
        protected final void download() {
            // implementation provided
        }
        
        protected abstract void process(); // implementation not provided
        
        protected final void upload() {
            // implementation provided
        }
        

        那么子类只能实现process方法,不能覆盖任何其他方法。

        【讨论】:

        • 子类仍然可以自己调用process(),对吗?因此不通过final doTransfer() 方法?也许我很挑剔/固执,但我觉得这是个问题......
        • 是的,你不能阻止某人编写一个调用process的子类;但维护父类创建的任何契约是子类的责任。
        【解决方案5】:

        process()方法不能被保护吗?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-09-29
          • 2021-08-13
          • 2018-12-13
          • 2011-09-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多