Sunteți pe pagina 1din 10

Implementarea Compozitiei si Agregarii in Java

O buna arhitectură a unei aplica ții orientate obiect presupune reutilizarea facilă a codului existent în
diferitele clase. Reutilizarea codului nu înseamnă în nici un caz copy-paste de cod. Ea este realizată
prin doua operații principale: compoziție și mo ștenire. La realizarea opera țiilor de mo ștenire și
compoziție, se vor importa clasele referite. Astfel, codul lor sursă va fi astfel reutilizat.

Compoziția
Compoziția înseamnă a crea o nouă clasă care con ține ca și date membre obiecte din clase existente.
Se spune că un obiect din noua clasă e compus din obiecte din clasele vechi (existente). Referin țele
datelor membre obiectuale nou create se ini țializează la valoarea null, deci ele trebuie ini țializate
explicit.

Compoziția este de două tipuri:


- Compoziție propriu-zisă : atunci când obiectul membru (sub-obiectul) este în mod efectiv parte
componentă a obiectului din noua clasa. în compozi ție, sub-obiectul există și are sens numai ca
parte a obiectului compus. De exemplu: o persoană are două mâini și o inimă; o clădire este
compusă din mai multe săli. Astfel, sub-obiectele (săli) sunt distruse de îndată ce obiectul
clădire va fi distrus și nu au sens în afara unei clădiri;
- Agregare: sub obiectul există în afara obiectului agregat, este creat în exterior, deci este trecut
ca un argument pentru constructor sau o metodă de ini țializare/adăugare. De exemplu,
administrația unui oraș poate fi proprietara unui parc de ma șini, însă ma șinile sunt create ini țial
într-un context diferit după care intră în posesia ora șului; mai apoi, o ma șină poate fi vândută și
ieși în proprietatea primăriei.

În descriere UML, structura de clase de mai sus poate fi reprezentată prin figura de mai jos:

Compoziția și Agregarea

Rombul plin înseamnă compoziție iar cel gol agregare. Compozi ția/Agregarea este expresia unei rela ții
de tipul “has-a”, și se traduce prin expresia: un obiect din tipul X este membru al unui obiect din tipul Y,
sau un obiect din tipul Y se compune dintr-un obiect din tipul X.

Important: Atunci cand un obiect compus refera mai multe obiecte de acelasi tip, referintele spre ele
se vor tine intr-o data membru de tip java.util.List.

class Person {
private Heart heart;
private List<Hand> hands;
}
class City {
private List<Tree> trees;
private List<Car> cars;
}

1. Initializarea Sub-obiectelor in cadrul Obiectele Compuse la realizarea


compoziției
Inițializarea referințelor sub-obiectelor in obiectele compuse la realizarea compozi ției se poate realiza în
mai multe moduri:
 La momentul definirii obiectului – ini țializarea se face înainte de apelarea constructorului;
import java.util.*;

class Heart {}

enum Position{ Right, Left}

class Hand {
Position position;
Hand (Position position){
this.position = position;
}
}

public class Person {


// initializare inainte de apelul constructorului
// in cazul de fata un constructor implicit fara parametrii
// generat de JVM
private Heart heart = new Heart();
private List<Hand> hands = new ArrayList<Hand>(
Arrays.asList(new Hand(Position.Left),
new Hand(Position.Right))
);
}

 In constructor;
public class Person {
private Heart heart;
private List<Hand> hands;

Person() {
//initializare in constructor
heart = new Heart();
hands = new ArrayList<Hand>(2);
hands.add(new Hand(Position.Left));
hands.add(new Hand(Position.Right));
}
}

 Chiar înainte de utilizarea obiectului ( lazy initialization). Vom vedea un astfel de exemplu
atunci cand vom face Design Pattern-ul Factory Method;
import java.util.List;

class DBConnection{
// ...
public void execute(String query) throws Exception{
// ...
}
}

class DBConnctionFactory{
static DBConnection getConnection() throws Exception {
// ...
return ...;
}
}

public class DBQuery {


private String dbQuery;
private List<String> parameters;
private DBConnection con;

static DBConnctionFactory dbFact = new DBConnctionFactory();

public DBQuery(String dbQuery) {


this.dbQuery = dbQuery;
}

public DBQuery(String dbQuery, List<String> parameters) {


this.dbQuery = dbQuery;
this.parameters = parameters;
}

private void openConection() throws Exception{


if(con == null)
con = dbFact.getConnection();
}

public void executeQuery() throws Exception{


openConection();
con.execute(dbQuery);
}

}
 In bloc non-static de inițializare ( instance initialization);
public class Person {
String name;
String foreName;
int age;

private Heart heart;


private List<Hand> hands;

{
//initializare in bloc non-static
heart = new Heart();
hands = new ArrayList<Hand>(2);
hands.add(new Hand(Position.Left));
hands.add(new Hand(Position.Right));
}

public Person(String name, String foreName) {


this.name = name;
this.foreName = foreName;
}

public Person(String name, String foreName, int age) {


this.name = name;
this.foreName = foreName;
this.age = age;
}
}

In cazul compozitiei ce implica liste de obiecte de acelasi fel, pot exista in clasele compuse metode de
adaugare/stergere ale sub-obiectelor:
import java.util.*;

class Car {
String brand;
int productionYear;

public Car(String brand, int productionYear) {


this.brand = brand;
this.productionYear = productionYear;
}
}

public class City{


List<Car> cars = new ArrayList<Car>();

public void addTree(Car c){


if(c!=null)
cars.add(c);
}

public void removeTree(Car c){


if(c!=null && cars.contains(c))
cars.remove(c);
}
}

2. Initializarea referintelor Obiectelor compuse in Sub-obiectele asociate

In cazul in care avem relatii bidirectionale intre sub-obiectele ce compun si obiectele compuse, atunci
sub-obiectele vor avea referinte spre obiectele compuse.
In cazul Agregarii aceste referinte trebuie initializate in constructorul sub-obiectelor. In exemplul de
mai jos, un obiect filiala nu are sens atat timp cat atributul sau Banca are valoarea null.
class Filiala {
Banca banca;
String adresa;

public Filiala(Banca banca, String adresa) {


this.adresa = adresa;
this.banca = banca;
}
}

public class Banca {


String nume;
String adresa;
List<Filiala> filiale;

public Banca(String nume, String adresa) {


this.nume = nume;
this.adresa = adresa;
filiale = new ArrayList<Filiala>();
}

public Filiala adaugaFiliala(String adresa){


filiale.add(new Filiala(this, adresa));
return filiale.get(filiale.size()-1);
}
}

In cazul compozitiei propriu-zise vor exista perechi de functii (adauga-setare/sterge-dereferentiere)


in ambele clase a relatiei care vor fi apelate concomitent doar in clasa compusa fiindca, in general, ea
controleaza relatia.

import java.util.*;

class Employee{
String name;
int age;
List<Company> companies = new ArrayList<Company>();

public Employee(String name, int age, Company company) {


this.name = name;
this.age = age;
companies.add(company);
}

public void addCompany(Company c){


companies.add(c);
}

public void removeCompany(Company c){


companies.remove(c);
}
}

public class Company {


String name;
List<Employee> employees = new ArrayList<Employee>();

public Company(String name) {


this.name = name;
}

public void addEmployee(Employee e){


employees.add(e);
e.addCompany(this);
}

public void removeEmployee(Employee e){


employees.remove(e);
e.removeCompany(this);
}
}

Observatie: Daca relatia este controlata de ambele clase, atunci perechile de functii (adauga-
setare/sterge-dereferentiere) vor fi apelate in ambele clase si se vor face verificari pentru evitarea
recursivitatii.
class Employee{
//...
List<Company> companies = new ArrayList<Company>();
//...
public void addCompany(Company c){
//avoid recursive call
if(!companies.contains(c)){
companies.add(c);
c.addEmployee(this);
}
}

public void removeCompany(Company c){


//avoid recursive call
if(companies.contains(c)){
companies.remove(c);
c.removeEmployee(this);
}
}
}

public class Company {


//...
List<Employee> employees = new ArrayList<Employee>();
//...
public void addEmployee(Employee e){
//avoid recursive call
if(!employees.contains(e)){
employees.add(e);
e.addCompany(this);
}
}

public void removeEmployee(Employee e){


//avoid recursive call
if(employees.contains(e)){
employees.remove(e);
e.removeCompany(this);
}
}

3. Gestiunea relatiilor de compozitie/agregare Decorate cu Atribute

Aceast timp de gestiune este realizata atunci cand relatiile de compozitie au atribute specifice.

Ea se realizeaza folosind dictionare(java.util.Dictionary sau java.util.Map) la nivelul clasei compuse si


liste (java.util.List) la nivelul clasei inglobate.
import java.util.*;

class Contract {
int salary;
String title;
int seniority;

// not necessary to be specified!!!


Employee employee;
Company company;

public Contract(
Employee employee, Company company,
int salary, String title, int seniority)
{
this.salary = salary;
this.title = title;
this.seniority = seniority;

this.employee = employee;
this.company = company;
}
}

class Employee{
String name;
int age;
List<Company> companies = new ArrayList<Company>();

public Employee(String name, int age, Company company) {


this.name = name;
this.age = age;
companies.add(company);
}

public void addCompany(Company c){


companies.add(c);
}

public void removeCompany(Company c){


companies.remove(c);
}
}

public class Company {


String name;
Dictionary<Employee, Contract> employees = new Hashtable<Employee,
Contract>();

public Company(String name) {


this.name = name;
}

public void addEmployee(Employee e, Contract c){


employees.put(e,c);
e.addCompany(this);
}

public void removeEmployee(Employee e){


employees.remove(e);
e.removeCompany(this);
}

public Contract getContract(Employee e){


return employees.get(e);
}

Enumeration<Employee> getEmployees(){
return employees.keys();
}

Enumeration<Contract> getContracts(){
return employees.elements();
}
}

Decorarea poate fi una atat de simpla incat sa nu necesite definirea unui obiect dedicat relatiei de
compozitie. Sa presupunem ca dorim sa gestionam un Depozit de produse neperisabile si necasabile.
In acest caz s-ar putea ca singura informatie pe care sa ne intereseze despre un tip de Produs este
numarul de unitati pe care le avem in stock.
import java.util.*;

class Produs {
String nume;
float greutate;
String producator;

public Produs(String nume, float greutate, String producator) {


this.nume = nume;
this.greutate = greutate;
this.producator = producator;
}

@Override
public String toString() {
return "Produs [nume=" + nume + ", greutate=" + greutate
+ ", producator=" + producator + "]";
}

public class Depozit {


Map<Produs,Integer> stock = new Hashtable<Produs,Integer>();

public void adaugaInStock(Produs p, int cantitate){


stock.put(p,(stock.get(p)==null?0:stock.get(p))+cantitate);
}

public boolean retrageDinStock(Produs p, int cantitate){


if(stock.get(p)==null || stock.get(p)<cantitate)
return false;
stock.put(p,stock.get(p)-cantitate);
return true;
}

public void afiseazaInventar(){


System.out.println("Inventar:");
for (Map.Entry<Produs,Integer> entry : stock.entrySet())
System.out.println(entry.getKey() + " cu cantitatea: " +
entry.getValue());

}
Problema propusa spre rezolvare. Fie o universitate ce contine mai multe facultati. Fiecare
facultate are un nume și mai multe pe specializări. Un student este caracterizat de nume,
prenume. El poate urma mai multe specializari l-a care s-a inscris prima oara la o anumita
data și unde a ajuns intr-un anume an de studiu.

Clasa facultate are o facilitate admite care pentru un nume și un prenume primite, creează un
nou student și îl adaugă la o specializare aleatorie. Daca exista deja un student cu numele si
prenumele respectiv in facultate, acesta nu este recreat ci functia returneaza studentul existent.

În clasa Facultate creați doi iteratori care sa permita parcurgerea studentilor in functie de
specializari sau in ordine alfabetica.

Testați toate clasele și funcționalitățile create într-o clasă separată Test. Unui obiect de tip
universitate, asociati-i o Facultate de Stiințe Economice ce ofera, printre altele,
specializările de Finanțe și Informatică economică. Folosind iteratorii, afișați pe ecran toți
studenții facultății ordonati dupa nume și mai apoi dupa fiecare specializare in parte.

S-ar putea să vă placă și