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]:
In [3]:
public interface ITableProducer {
String[] requestAttributes();
String[][] requestInstances();
}
Out[3]:
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]:
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]);
}
Out[5]:
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]:
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]:
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]);
}
Out[8]: