Javaプログラミング思想(九)——インタフェース


一、抽象クラスと抽象方法
        Javaは抽象的な方法と呼ばれるメカニズムを提供しています.この方法は不完全で、方法の定義がなく、方法だけが宣言されています.以下のようにします.
abstract void f();

        抽象メソッドを含むクラスを抽象クラスと呼び、抽象クラスを構築する目的は、すべてのエクスポートクラスの共通部分を表す汎用インタフェースを提供することであり、異なるサブクラスは異なる方法でこのインタフェースを表すことができる. .クラスに1つ以上の抽象メソッドが含まれている場合、クラスは以下のように抽象化されなければなりません.
public abstract class AbstractClassExample {
    //  
}

         抽象クラスから継承し、新しいクラスのオブジェクトを作成する場合は、ベースクラスのすべての抽象メソッドにメソッド定義を提供する必要があります.提供しない場合は、エクスポート・クラスも抽象クラスとして宣言する必要があります.例は次のとおりです.
public abstract class AbstractClassExample {
    public abstract void fun1();
    public void fun2(){
        System.out.println("AbstractClassExample fun2()");
    }
}

class AbstractClassExampleChild extends AbstractClassExample{

    @Override
    public void fun1() {
        System.out.println("AbstractClassExampleChild fun1()");
    }

    public void fun2(){
        System.out.println("AbstractClassExampleChild fun2()");
        super.fun2();
    }
}

class AbstractClassExampleTest{
    public static void main(String[] args) {
        AbstractClassExample example = new AbstractClassExampleChild();
        example.fun1();
        example.fun2();
    }
}

 二、インタフェース
        Interfaceキーワードを使用すると、完全に抽象的なクラスが生成され、メソッド宣言のみが含まれ、メソッドはpublicにデフォルト設定されています.インタフェースにはドメインも含まれますが、すべてのドメインは暗黙的なstaticとfinalです.クラスインタフェースのメソッドを実装する場合は、implementsキーワードを使用します.例は次のとおりです.
/**
 * author Alex
 * date 2018/12/9
 * description       
 */
public interface InterfaceExample {
    public int NUM = 10;
    public void fun1();
}

class InterfaceExampleImpl implements InterfaceExample{

    @Override
    public void fun1() {
        System.out.println("InterfaceExampleImpl fun1()");
    }
}

class InterfaceExampleTest{
    public static void main(String[] args) {
        InterfaceExample example = new InterfaceExampleImpl();
        System.out.println(InterfaceExample.NUM);
        example.fun1();
    }
}

三、完全なデカップリング
1、策略設計モード
         まず、結合と比較的きつい例を見てみましょう.
/**
 * author Alex
 * date 2018/12/9
 * description         
 */
public class Processor {
    public String name(){
        return this.getClass().getSimpleName();
    }
    public Object process(Object input){
        return input;
    }
}

class Upcase extends Processor{
    public String process(Object input){
        return ((String)input).toUpperCase();
    }
}

class Downcase extends Processor{
    public String process(Object input){
        return ((String)input).toLowerCase();
    }
}

class Apply{
    public static void process(Processor p,Object s){
        System.out.println(p.name());
        System.out.println(p.process(s));
    }
}

class ProcessorTest{
    public static void main(String[] args) {
        String str = "This is test text";
        Apply.process(new Upcase(),str);
        Apply.process(new Downcase(),str);
        //      :
        //Upcase
        //THIS IS TEST TEXT
        //Downcase
        //this is test text
    }
}

        上記の例のように、伝達されるパラメータオブジェクトによって異なる動作を有することができる方法を、ポリシー設計モードと呼ぶ.このようなメソッドには、実行するアルゴリズムの固定された不変の部分が含まれ、ポリシーには変化する部分が含まれています.ポリシーは、実行するコードを含むパラメータオブジェクトに渡されます.前の例では、各Processorオブジェクトがポリシーです.
        前述の例では、Apply.process()メソッドがProcessorクラスと結合しすぎて、本来多重化可能なApply.process()メソッドが多重化されない.このとき,前例を簡単に改造し,インタフェースと実装を分離した.緩結合和の例は以下の通りである.
/**
 * author Alex
 * date 2018/12/9
 * description           
 */
public interface Processor1 {
    String name();
    Object process(Object input);
}

class Apply1{
    public static void process(Processor1 p,Object s){
        System.out.println(p.name());
        System.out.println(p.process(s));
    }
}

//                        
abstract class StringProcessor implements Processor1{
    public String name(){
        return this.getClass().getSimpleName();
    }
    public abstract String process(Object input);//           
}

class Upcase1 extends StringProcessor{
    public String process(Object input){
        return ((String)input).toUpperCase();
    }
}

class Downcase1 extends StringProcessor{
    public String process(Object input){
        return ((String)input).toLowerCase();
    }
}

class StringProcessorTest{
    public static void main(String[] args) {
        String str = "This is test text";
        Apply1.process(new Upcase1(),str);
        Apply1.process(new Downcase1(),str);
        //      :
        //Upcase
        //THIS IS TEST TEXT
        //Downcase
        //this is test text
    }
}

 2、適合モードと代理モードの結合
        実際の作業では、使用するクラスを変更できないことがよくあります.この場合、アダプタモードを使用して既存のインタフェースを使用して新しいインタフェースを生成できます.次の例を示します.
public interface Processor1 {
    String name();
    Object process(Object input);
}

class Apply1{
    public static void process(Processor1 p,Object s){
        System.out.println(p.name());
        System.out.println(p.process(s));
    }
}

class WaveForm {
    private static long counter = 0;
    private final long id = ++counter;
    public String toString(){
        return this.getClass().getSimpleName() + counter;
    }
}

class FilterNew{
    public String name(){
        return this.getClass().getSimpleName();
    }
    public WaveForm process(WaveForm input){
        return input;
    }
}

class LowPass extends FilterNew{
    double cutoff;
    LowPass(double cutoff){
        this.cutoff = cutoff;
    }
    public WaveForm process(WaveForm input){
        return input;
    }
}

class HighPass extends FilterNew{
    double cutoff;
    HighPass(double cutoff){
        this.cutoff = cutoff;
    }
    public WaveForm process(WaveForm input){
        return input;
    }
}

//      
class FilterAdapter implements Processor1{
    FilterNew filterNew;
    FilterAdapter(FilterNew filterNew){
        this.filterNew = filterNew;
    }
    @Override
    public String name() {
        return filterNew.name();
    }
    @Override
    public Object process(Object input) {
        return filterNew.process((WaveForm) input);//      
    }
}

class FilterAdapterTest{
    public static void main(String[] args) {
        WaveForm w = new WaveForm();
        Apply1.process(new FilterAdapter(new LowPass(5.0)),w);
        Apply1.process(new FilterAdapter(new HighPass(10.0)),w);
        //      :
        //LowPass
        //WaveForm1
        //HighPass
        //WaveForm1
    }
}

四、Javaにおける多重継承
1、一つのクラスを継承し、複数のインタフェースを実現する
        インタフェース以外のクラスから継承する場合は、1つのクラスしか継承できません.残りのクラスはインタフェースでなければなりません.これらのインタフェースをimplementsキーワードの後に置き、カンマで区切る必要があります.例は次のとおりです.
public class Father {
    void drinkWine(){
        System.out.println("Father  ");
    }
}

interface Person{
    void work();
}

interface Student{
    void study();
}

class Son extends Father implements Person,Student{

    @Override
    public void work() {
        System.out.println("Son  ");
    }

    @Override
    public void study() {
        System.out.println("Son  ");
    }
}

class FatherTest{
    public static void main(String[] args) {
        Son son = new Son();
        son.drinkWine();//          
        son.work();//    
        son.work();//    
        //      :
        //Father  
        //Son  
        //Son  
    }
}

2、継承によってインタフェースを拡張し、複数のインタフェースを継承する
        複数のインタフェースを継承するのはインタフェース自体に限られ,通常のクラスでは多重継承はできず,複数のインタフェースしか実現できない.例は次のとおりです.
public interface Interface1 extends Interface2,Interface3{
    void fun1();
}

interface Interface2{
    void fun2();
}

interface Interface3{
    void fun3();
}

class InterfaceUser implements Interface1{

    @Override
    public void fun3() {
        System.out.println("fun3()");
    }

    @Override
    public void fun2() {
        System.out.println("fun2()");
    }

    @Override
    public void fun1() {
        System.out.println("fun1()");
    }
}

class InterfaceTest{
    public static void main(String[] args) {
        InterfaceUser user = new InterfaceUser();
        user.fun1();
        user.fun2();
        user.fun3();
        //    :
        //fun1()
        //fun2()
        //fun3()
    }
}

五、インタフェースと工場
        インタフェースは多重継承を実現する方法であり、あるインタフェースに従うオブジェクトを生成する典型的な方法はファクトリメソッド設計モードである.これは、直接呼び出しコンストラクタとは異なり、ファクトリオブジェクト上で作成メソッドを呼び出し、ファクトリオブジェクトはインタフェースの実装オブジェクトを生成します.このようにして、私たちのコードはインタフェースの実装から完全に分離され、ある実装を透明に別の実装に置き換えることができます.簡単な例は次のとおりです.
public interface Service {
    void fun();
}

interface ServiceFactory{
    Service getService();
}

class Implementation implements Service{
    @Override
    public void fun() {
        System.out.println("Implementation fun()");
    }
}

class ImplementationFactory implements ServiceFactory{
    @Override
    public Service getService() {
        return new Implementation();
    }
}

class ServiceTest{
    public static void service(ServiceFactory factory){
        Service service = factory.getService();
        service.fun();
    }
    public static void main(String[] args) {
        ServiceTest.service(new ImplementationFactory());
        //    :Implementation fun()
    }
}

まとめ:
        クラスの作成では、インタフェースとファクトリを作成する代わりに、ほとんどの時点で使用できます.しかし、いかなる抽象性も真のニーズによって生成され、必要に応じてインタフェースを再構築し、追加レベルの間接性をあちこち追加するのではなく、追加の複雑さをもたらす必要があります.適切な原則は、インタフェースではなくクラスを優先的に選択することです.