Download as pdf or txt
Download as pdf or txt
You are on page 1of 10

Laborator 6.

Programare orientată obiect


Design Pattern-urile: Strategy, Adapter și Factory Method

1. Justificarea introducerii interfetelor – complete decoupling si design patternul Strategy


Creati un package "processor" in care implementati urmatoarele clase:
package processor;
//import filters.*;
import java.util.*;
class Processor {
public String name() { return getClass().getSimpleName(); }
Object process(Object input) { return input; }
}
class Upcase extends Processor {
String process(Object input) { // Covariant return
return ((String)input).toUpperCase();
}
}
class Downcase extends Processor {
String process(Object input) {
return ((String)input).toLowerCase();
}
}
class Splitter extends Processor {
String process(Object input) {
return Arrays.toString(((String)input).split(" "));
}
}
public class Apply {
public static void process(Processor p, Object s) {
System.out.println("Using Processor " + p.name());
System.out.println(p.process(s));
}
public static String s = "Disagreement with beliefs is by definition incorrect";
public static void main(String[] args) {
process(new Upcase(), s);
process(new Downcase(), s);
process(new Splitter(), s);
//(1)
/* Waveform w = new Waveform();
process(new LowPass(1.0),w);
process(new HighPass(2.0),w);
process(new BandPass(3.0),w);
//liniile de mai sus nu vor functiona prin apelul process()
//desi clasa Filter are aceleasi elemente in interfata: name() si process()
//insa Filter nu este mostenita din Processor */
}

Sa presupunem ca gasim o biblioteca de clase similara cu cea de mai jos care s-ar parea la prima vedere
ca se poate integra prin mecanismul descris mai sus prin metoda Apply.process();

Creati un nou package "filters" in care implementati urmatoarele clase cate una pe fisier pentru a fi
toate publice:
package filters;
public class Waveform {
private static long counter;
private final long id = counter++;
public String toString() { return "Waveform " + id; }
}

package filters;
public class Filter {
public String name() {
return getClass().getSimpleName();
}
public Waveform process(Waveform input) { return input; }
}

package filters;
public class LowPass extends Filter {
double cutoff;
public LowPass(double cutoff) {
this.cutoff = cutoff;
}
public Waveform process(Waveform input) { return input;}
}

package filters;
public class HighPass extends Filter {
double cutoff;
public HighPass(double cutoff) {
this.cutoff = cutoff;
}
public Waveform process(Waveform input) { return input; }
}

package filters;
public class BandPass extends Filter {
double lowCutoff, highCutoff;
public BandPass(double lowCut, double highCut) {
lowCutoff = lowCut;
highCutoff = highCut;
}
public Waveform process(Waveform input) { return input; }
}

Daca in clasa Apply, metoda main() decomentam codul marcat cu (1) si importam package-ul "filters"
(cu import) in care am implementat clasele Filters vom constata ca nu putem utiliza clasele Filter cu
Apply.process() deoarece Filter nu este mostenita din Processor.

Atentie: Apply.process nu poate fi aplicata pe obiecte din ierarhia Filter (cu toate, ca teoretic, nu ar
trebui sa fie probleme). => clasa Apply se rescrie, Processor este transformata in interfata
Pentru a ocoli putin aceste constrangeri clasa Processor este rescrisa ca si interfata iar clasa Apply este
rescrisa ca si mai jos:

Creati un nou package‚ "interfaceprocessor" in care scrieti urmatoarele clase care vor da acelasi output
ca si in cazul primului exemplu, doar ca aici se utilizeaza interfete. (pentru crearea interfetei se da click
dreapta pe package > New > Java Interface)
package interfaceprocessor;
public interface Processor {
String name();
Object process(Object input);
}

package interfaceprocessor;
public class Apply {
public static void process(Processor p, Object s) {
System.out.println("Using Processor " + p.name());
System.out.println(p.process(s));
}
public static String s = "If she weighs the same as a duck, she’s made of wood";
public static void main(String[] args) { Apply.process(new Upcase(), s);
Apply.process(new Downcase(), s);
Apply.process(new Splitter(), s);
}
}

Aceasta varianta se aplica daca programatorul poate sa rescrie clasele din ierarhie astfel incat sa se
conformeze cu interfata:

package interfaceprocessor;
import java.util.*;
public abstract class StringProcessor implements Processor{
public String name() { return getClass().getSimpleName(); }
public abstract String process(Object input);
}
class Upcase extends StringProcessor {
public String process(Object input) { // Covariant return
return ((String)input).toUpperCase();
}
}
class Downcase extends StringProcessor {
public String process(Object input) {
return ((String)input).toLowerCase();
}
}
class Splitter extends StringProcessor {
public String process(Object input) {
return Arrays.toString(((String)input).split(" "));
}
}
Desing pattern-ul Adapter: se doreste ca o clasa importata (de exemplu Filter) sa poata fi utilizata in
conjunctie cu interfata definita de noi (interfata Processor) fara a fi nevoie rescrierea claselor:
Creati un nou package "adapter" in care implementati urmatoarele clase (nu uitati sa importati package-
ul "interfaceprocessor" si package-ul "filters" create anterior):

package adapter;
import interfaceprocessor.*;
import filters.*;
class FilterAdapter implements Processor {
Filter filter; //compozitia cu clasa de baza Filter
public FilterAdapter(Filter filter) { // pot sa initializez cu orice obiect din ierarhia Filter
this.filter = filter;
}
//accesez functionalitatile din ierarhia Filter prin membrul Filter
public String name() { return filter.name(); }
public Waveform process(Object input) {
return filter.process((Waveform)input);
}
}
public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
//upcasting LowPass->Filter
Apply.process(new FilterAdapter(new LowPass(1.0)), w);
//upcasting HighPass->Filter
Apply.process(new FilterAdapter(new HighPass(2.0)), w);
//upcasting BandPass->Filter
Apply.process(new FilterAdapter(new BandPass(3.0, 4.0)), w);
}
}
2. Design pattern-ul Factory Method
package factory;
interface Service { //interfata service
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implementation1 implements Service {
Implementation1() {} // Package access
public void method1() { System.out.println("Implementation1 method1");}
public void method2() { System.out.println("Implementation1 method2");}
}
class Implementation1Factory implements ServiceFactory {
public Service getService() {
return new Implementation1(); //returnare obiect cu interfata Service
//si implementarea Implementation1
}
}
class Implementation2 implements Service {
Implementation2() {} // Package access
public void method1() {System.out.println("Implementation2 method1");}
public void method2() {System.out.println("Implementation2 method2");}
}
class Implementation2Factory implements ServiceFactory {
public Service getService() {
return new Implementation2();//returnare obiect cu interfata Service
//si implementare a interfetei Implementation2
}
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService(); //creare obiect s fara apel direct de constructor
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implementation1Factory());
// Implementations are completely interchangeable:
serviceConsumer(new Implementation2Factory());
}
}
Atentie: implementarile sunt absolut interschimbabile intre ele la utilizarea lor (in metoda ServiceConsumer)
Clase interioare

4. Crearea claselor interioare. Se observa faptul ca de obicei, clasa exterioara are o metoda care
returneaza o referinta catre clasa inner

public class Parcel { //clasa exterioara Parcel


class Contents { //clasa interioara Contents
private int i = 11;
public int value() { return i; }
}//end clasa inner
class Destination { //clasa interioara Destination
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}//end clasa inner
public Destination to(String s) {
//ob. din clasa int. Destination
return new Destination(s);
}
public Contents contents() {
//ob. din clasa int. Contents
return new Contents();
}
public void ship(String dest) {
Contents c = contents();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel p = new Parcel();
p.ship("Tasmania");
Parcel q = new Parcel();
// Defining references to inner classes:
//!!! accesul la clasa interioara Outer.inner
Parcel.Contents c = q.contents();
Parcel.Destination d = q.to("Borneo");
}
} //end clasa outter

Reprezentarea schematica a claselor interioare:


Utilizarea claselor interioare pentru a implementa design pattern-ul Iterator
interface Selector { //operatii ale unui iterator
boolean end();
Object current();
void next();
}
public class Sequence {
private Object[] items; //colectia de iterat
private int next = 0; //indice
public Sequence(int size) { items = new Object[size]; }
public void add(Object x) {
if(next < items.length)
items[next++] = x;
}
private class SequenceSelector implements Selector {
private int i = 0;
public boolean end() { return i == items.length; }
public Object current() { return items[i]; }
public void next() { if(i < items.length) i++; }
}
public Selector selector() {
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for(int i = 0; i < 10; i++)
sequence.add(Integer.toString(i));
Selector selector = sequence.selector();
while(!selector.end()) {
System.out.print(selector.current() + " ");
selector.next();
}
}
}

In exemplul de mai jos, studentii trebuie sa converteasca clasele Contents si Destination de mai sus in
interfete
class TParcel {
private class PContents implements Contents {
private int i = 11;
public int value() { return i; }
}
protected class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
public Destination destination(String s) {
return new PDestination(s);
}
public Contents contents() {
return new PContents();
}
}
public class TestParcel {
public static void main(String[] args) {
TParcel p = new TParcel();
Contents c = p.contents();
Destination d = p.destination("Tasmania");
// Illegal -- can’t access private class:
//! TParcel.PContents pc = p.new PContents();
}
}

5. Creati o interfata cu cel putin o metoda, intr-un package separat. Creati o alta clasa intr-un alt
package. In aceasta, adaugati o clasa interioara care implementeaza interfata. In cel de-al
treilea package, mosteniti o noua clasa (din clasa din packageul al doilea), si adaugati in
interiorul acesteia o metoda care returneaza un obiect de tipul clasei interioare protected,
realizand upcast la interfata la momentul comenzii return.
6. Inner class in interiorul unei metode
Clasa PDestination este vizibila si poate fi accesata doar in interiorul metodei destination().

public class Parcel5 {


public Destination destination(String s) {
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Tasmania"); } }
7. Clase interioare anonime

public class Parcel7 {


public Contents contents() {
return new Contents() { // def. unei clase interioare anonime
private int i = 11;
public int value() { return i; }
}; // Semicolon required in this case
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}

In clasele anonime, argumentele trebuie sa aiba referintele final pentru a putea fi utilizate

public class Parcel9 {


// Argument must be final to use inside anonymous inner class:
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
}

Clasele anonime trebuie sa realizeze construirea in blocul non-static de initializare

abstract class Base {


public Base(int i) {System.out.println("Base constructor, i = " + i);}
public abstract void f();
}
public class AnonymousConstructor {
public static Base getBase(int i) {
return new Base(i) {
//bloc non-static de initializare
{ System.out.println("Inside instance initializer"); }
public void f() {//metoda in clasa anonima
System.out.println("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47); base.f(); } }

8. Design patternul Factory realizat cu inner classes

You might also like