A seguir são reunidos alguns conceitos importantes para se entender o princípio de Componentes de Software.
Em linhas gerais, um componente é um módulo de software reusável, que se liga aos seus parceiros exclusivamente através de interfaces. Apresentaremos aqui uma abordagem típica de ligação de componentes baseada em interfaces e conexões:
interfaces
Java;Idealmente, todas as funcionalidades de um componente deveriam ser acessadas exclusivamente através de interfaces.
Vamos analisar o processo de definição do DataSetComponent
como um componente de software. Considerando que o código a seguir é uma versão inicial do componente, uma representação visual para o mesmo pode ser feita em UML, conforme segue:
Note que cada interface que ele disponibiliza é apresentada na forma de uma haste com um círculo na ponta.
Como a interface IDataSetProperties
tem métodos get
e set
para definir a propriedade DataSource
, em vez de representar essa interface da forma tradicional, podemos representá-la na forma de uma propriedade associada ao componente como segue:
A representação de propriedades não é padrão UML, mas é usada por algumas extensões. Isso introduz um terceiro elemento usual em algumas implementações de componentes:
dataSource
é possível externamente se configurar a fonte de dados do componente.
In [1]:
public interface ITableProducer {
String[] requestAttributes();
String[][] requestInstances();
}
Out[1]:
In [2]:
public interface IDataSetProperties {
public String getDataSource();
public void setDataSource(String dataSource);
}
Out[2]:
In [3]:
public interface IDataSet extends IDataSetProperties, ITableProducer {
}
Out[3]:
In [4]:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class DataSetComponent implements IDataSet {
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]:
É comum em componentes se usar a mesma estratégia vista em aulas anteriores, na qual um objeto de uma classe guarda referência de um objeto de outra classe. Utilizaremos um método padrão chamado connect
que conecta dois componentes. Para isso um dos objetos recebe a referência de outro.
No exemplo a seguir, foi criado um componente chamado ConsoleComponentA
, cuja função é mostrar dados de outro componente conectado a ele no console. Ele dispõe do seguinte método:
public void connect(ITableProducer producer)
Esse método recebe a referência para qualquer objeto que implementa a interface ITableProducer
e a guarda. A referência é posteriormente usada para a comunicação entre os dois objetos.
Visualmente, a conexão entre os dois componentes pode ser vista da seguinte maneira:
In [5]:
public class ConsoleComponentA {
private ITableProducer iProducer;
public void connect(ITableProducer producer) {
iProducer = producer;
}
public void update() {
if (iProducer != null) {
System.out.println("=== Attributes ===");
String attributes[] = iProducer.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[][] = iProducer.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]:
In [6]:
IDataSet dataset = new DataSetComponent();
dataset.setDataSource("../../../db/zombie/zombie-health-spreadsheet.csv");
ConsoleComponentA console = new ConsoleComponentA();
console.connect(dataset);
console.update();
Out[6]:
O ideal é que tudo seja explícito e que haja uma interface para realizar essa conexão. Desse modo, criaremos uma interface chamada ITableReceptacle
. Ela define o método para se estabelecer a conexão com objetos que têm a interface ITableProducer
.
A interface requerida é representada visualmente por um meio círculo e indica o nome da interface que ela requer:
Outra maneira bastante usual de representar os dois componentes conectados é ligando diretamente a interface provida com a requerida:
Para tornar o componente ConsoleComponent
completamente acessível por interfaces, acrescentamos outra interface para o seu método update()
. O Diagrama completo fica:
Como foi feito antes, usamos a interface IConsole
para juntar as duas outras interfaces através da herança.
In [7]:
public interface ITableReceptacle {
public void connect(ITableProducer producer);
}
Out[7]:
In [8]:
public interface IConsoleUpdate {
public void update();
}
Out[8]:
In [9]:
public interface IConsole extends ITableReceptacle, IConsoleUpdate {
}
Out[9]:
In [10]:
public class ConsoleComponent implements IConsole {
private ITableProducer iProducer;
public void connect(ITableProducer producer) {
iProducer = producer;
}
public void update() {
if (iProducer != null) {
System.out.println("=== Attributes ===");
String attributes[] = iProducer.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[][] = iProducer.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[10]:
In [11]:
IDataSet dataset = new DataSetComponent();
dataset.setDataSource("../../../db/zombie/zombie-health-spreadsheet.csv");
IConsole console = new ConsoleComponent();
console.connect(dataset);
console.update();
Out[11]: