2014-09-30 2 views
1

У меня есть бизнес-кейс, в котором 3 вещи, которые должны произойти, в следующей последовательности:Принудительно метод порядок выполнения в абстрактном классе

  1. скачать()
  2. процесса()
  3. загрузки()

Теперь абстрактное класс FileTransfer обеспечивает реализацию для 1. downloadFiles() и 3. upload(), но не 2. process() - дочерние классы (например, Mu sicFileTransfer или 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 для вызова процесса() перед загрузкой() или любого другого порядка. Процесс всегда должен быть 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() и публичный процесс() и просто убедитесь, что процесс() никогда не называется? Или покончить с doTransfer() и надеяться, что порядок всегда правильный?

ответ

4

Вам нужен template method pattern. Создайте метод doTransfer (final) в классе FileTransfer, который определяет порядок методов, которые необходимо вызывать. Сделайте download и upload методы также 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 и они не могут переопределить любые другие методы.

+1

все еще было бы возможно подкласс вызвать 'процесса()' само по себе, правильно? таким образом, не проходит метод 'final doTransfer()'? Может быть, я придирчивый/упрямый, но я чувствую, что это проблема ... – mmcrae

+1

Да, вы не можете запретить кому-либо писать подкласс, который вызывает 'process'; но ответственность за подкласс лежит на поддержании любого контракта, создаваемого суперклассом. – rgettman

0

Невозможно защитить метод process()?

1

Если все классы, расширяющие FileTransfer, будут в одном пакете, вы можете сделать download(), upload() и process() защищенных методов. Таким образом вы ограничиваете, кто может их назвать.

Затем сохранить ваш метод шаблона, doTransfer() общественности, так что это единственный метод, который может быть вызван внешними классами (вне пакета)

Я хотел бы также добавить 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(), вы не можете точно сказать, что его нельзя назвать в одиночку. Вы только предполагаете, что они реализуют его так, как вы ожидаете, что это приведет к неправильной работе.

+0

Большое вам спасибо за ваш вклад. Вы и rgettman дали мне понять, и я действительно ценю это. – mmcrae

1

В этом случае вы можете подумать о том, чтобы сделать метод 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 

    } 
} 

Sample метод потребителя:

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

Типичный подход здесь должен следить за состоянием, так что если один из этих методов вызывается из строя, вы тогда бросаете 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 

ИЛИ, что может быть опрятным для вашего кода клиента, хотя, возможно, требует немного больше шаблонного от вы, будете использовать «делегирование»: подкласс оригинал базового класса, к доверителю, который поддерживает resposibility для вызова вашего класса реализации:

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(); 
    }