Múltiplas Interfaces

Interfaces podem ser decompostas em interfaces menores que expressam subconjuntos de funcionalidades. Por exemplo, retomando a classe DataSetComponent do Zombie Health abaixo. Podemos pensar em duas interfaces para implementá-la: IDataSetProperties que concentra os métodos que modificam as propriedades da classe; ITableProducer que concentra funcionalidades de qualquer objeto capaz de produzir dados tabulares (independentemente da origem desses dados).

Dividi-las em interfaces menores permite que uma classe não seja sempre obrigada a implementar tudo junto. Por exemplo, eu poderia ter um DataSource cujos dados sejam na forma de árvore, em vez de tabulares.

Veja abaixo como a classe DataSetComponent implementa as duas interfaces:


In [2]:
public interface IDataSetProperties {
  public String getDataSource();
  public void setDataSource(String dataSource);
}


Out[2]:
com.twosigma.beaker.javash.bkr54c231c9.IDataSetProperties

In [3]:
public interface ITableProducer {
  String[] requestAttributes();
  String[][] requestInstances();
}


Out[3]:
com.twosigma.beaker.javash.bkr54c231c9.ITableProducer

In [4]:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class DataSetComponent implements IDataSetProperties, ITableProducer {
  private String dataSource = null;
  private String[] attributes = null;
  private String[][] instances = null;
  
  public DataSetComponent() {
    /* nothing */
  }

  public String getDataSource() {
    return dataSource;
  }

  public void setDataSource(String dataSource) {
    this.dataSource = dataSource;
    if (dataSource == null) {
      attributes = null;
      instances = null;
    } else
      readDS();
  }
  
  public String[] requestAttributes() {
    return attributes;
  }
  
  public String[][] requestInstances() {
    return instances;
  }
  
  private void readDS() {
    ArrayList<String[]> instArray = new ArrayList<String[]>();
    try {
      BufferedReader file = new BufferedReader(new FileReader(dataSource));
        
      String line = file.readLine();
      if (line != null) {
        attributes = line.split(",");
        line = file.readLine();
        while (line != null) {
          String[] instLine = line.split(",");
          instArray.add(instLine);
          line = file.readLine();
        }
        instances = instArray.toArray(new String[0][]);
      }
        
      file.close();
    } catch (IOException erro) {
      erro.printStackTrace();
    }
  }
  
}


Out[4]:
com.twosigma.beaker.javash.bkr54c231c9.DataSetComponent

Usando múltiplas interfaces

O problema de múltiplas interfaces é na hora de usar, já que você tem que indicar qual a interface usará a cada momento. Veja abaixo como fica o código.

Primeiro iniciou-se com a interface IDataSetProperties, já que deveria ser configurada a propriedade de fonte de dados. Depois foi necessário mudar a referência para o objeto com a interface ITableProducer. Note que foi necessário um cast:

ITableProducer tp = (ITableProducer)ds;

In [5]:
IDataSetProperties ds = new DataSetComponent();
ds.setDataSource("../../../db/zombie/zombie-health-spreadsheet-ml-training.csv");

// aqui eh necessario se mudar de interface para se ter acesso aos metodos da tabela
ITableProducer tp = (ITableProducer)ds;

System.out.println("=== Attributes ===");
String attributes[] = tp.requestAttributes();
for (int a = 0; a < attributes.length-1; a++)
  System.out.print(attributes[a] + ", ");
System.out.println(attributes[attributes.length-1]);

System.out.println();
System.out.println("=== Instances ===");
String instances[][] = tp.requestInstances();
for (int i = 0; i < instances.length; i++) {
  for (int a = 0; a < attributes.length-1; a ++)
    System.out.print(instances[i][a] + ", ");
  System.out.println(instances[i][attributes.length-1]);
}


=== Attributes ===
paralysis, yellow_tong, member_loss, chest_pain, trembling_finger, severe_anger, history_bacteria, diagnostic

=== Instances ===
t, t, f, f, f, f, f, bacterial_infection
f, t, f, f, f, f, f, bacterial_infection
f, t, f, f, t, f, t, bite_deficit
f, t, t, f, t, f, f, bite_deficit
f, f, t, t, f, f, f, viral_infection
f, f, t, f, f, t, f, fights
f, f, f, f, f, t, f, nothing
f, f, f, f, t, f, f, bite_deficit
f, t, f, t, f, f, f, bacterial_infection
f, f, f, t, f, f, f, viral_infection
f, t, t, f, f, f, t, bite_deficit
t, t, f, f, f, f, f, bacterial_infection
f, f, f, t, f, f, t, viral_infection
f, f, t, f, f, f, f, fights
f, t, f, f, t, f, t, bite_deficit
f, t, t, f, t, f, f, bite_deficit
f, f, f, t, f, f, f, fights
f, t, f, f, f, f, f, bacterial_infection
f, f, f, f, t, f, f, bite_deficit
Out[5]:
null

Herança de Interfaces

Interfaces podem ser herdeiras de outras interfaces, ou seja, elas herdam a declaração de métodos da superinterface. Interfaces aceitam herança múltipla, então podemos tornar a solução anterior mais elegante criando uma interface chamada IDataSet que junta IDataSetProperties e ITableProducer, como segue:


In [6]:
public interface IDataSet extends IDataSetProperties, ITableProducer {
}


Out[6]:
com.twosigma.beaker.javash.bkr54c231c9.IDataSet

Implementando a interface IDataSet

O componente passa a fazer referência apenas à interface unificada IDataSet.


In [7]:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class DataSetComponentII implements IDataSet {
  private String dataSource = null;
  private String[] attributes = null;
  private String[][] instances = null;
  
  public DataSetComponentII() {
    /* nothing */
  }

  public String getDataSource() {
    return dataSource;
  }

  public void setDataSource(String dataSource) {
    this.dataSource = dataSource;
    if (dataSource == null) {
      attributes = null;
      instances = null;
    } else
      readDS();
  }
  
  public String[] requestAttributes() {
    return attributes;
  }
  
  public String[][] requestInstances() {
    return instances;
  }
  
  private void readDS() {
    ArrayList<String[]> instArray = new ArrayList<String[]>();
    try {
      BufferedReader file = new BufferedReader(new FileReader(dataSource));
        
      String line = file.readLine();
      if (line != null) {
        attributes = line.split(",");
        line = file.readLine();
        while (line != null) {
          String[] instLine = line.split(",");
          instArray.add(instLine);
          line = file.readLine();
        }
        instances = instArray.toArray(new String[0][]);
      }
        
      file.close();
    } catch (IOException erro) {
      erro.printStackTrace();
    }
  }
  
}


Out[7]:
com.twosigma.beaker.javash.bkr54c231c9.DataSetComponentII

Usando a interface IDataSet

Como você verá a seguir, torna-se possível se resolver tudo com uma única interface:


In [8]:
IDataSet ds = new DataSetComponentII();
ds.setDataSource("../../../db/zombie/zombie-health-spreadsheet-ml-training.csv");

System.out.println("=== Attributes ===");
String attributes[] = ds.requestAttributes();
for (int a = 0; a < attributes.length-1; a++)
  System.out.print(attributes[a] + ", ");
System.out.println(attributes[attributes.length-1]);

System.out.println();
System.out.println("=== Instances ===");
String instances[][] = ds.requestInstances();
for (int i = 0; i < instances.length; i++) {
  for (int a = 0; a < attributes.length-1; a ++)
    System.out.print(instances[i][a] + ", ");
  System.out.println(instances[i][attributes.length-1]);
}


=== Attributes ===
paralysis, yellow_tong, member_loss, chest_pain, trembling_finger, severe_anger, history_bacteria, diagnostic

=== Instances ===
t, t, f, f, f, f, f, bacterial_infection
f, t, f, f, f, f, f, bacterial_infection
f, t, f, f, t, f, t, bite_deficit
f, t, t, f, t, f, f, bite_deficit
f, f, t, t, f, f, f, viral_infection
f, f, t, f, f, t, f, fights
f, f, f, f, f, t, f, nothing
f, f, f, f, t, f, f, bite_deficit
f, t, f, t, f, f, f, bacterial_infection
f, f, f, t, f, f, f, viral_infection
f, t, t, f, f, f, t, bite_deficit
t, t, f, f, f, f, f, bacterial_infection
f, f, f, t, f, f, t, viral_infection
f, f, t, f, f, f, f, fights
f, t, f, f, t, f, t, bite_deficit
f, t, t, f, t, f, f, bite_deficit
f, f, f, t, f, f, f, fights
f, t, f, f, f, f, f, bacterial_infection
f, f, f, f, t, f, f, bite_deficit
Out[8]:
null