Definiowanie zachowania obiektów

Metody instancyjne i klasowe, klasy abstrakcyjne, interfejsy


dr inż. Aleksander Smywiński-Pohl

apohllo@o2.pl

http://apohllo.pl/dydaktyka/programowanie-obiektowe

Definiowanie (publicznego) zachowania

In [ ]:
enum SpeedUnit {
    KMH, MS
}
In [ ]:
class Speed {
    public double value;
    public SpeedUnit unit;
}
In [ ]:
class SpaceShip {
    private Speed speed;

    public SpaceShip(){
        this.speed = new Speed();
        this.speed.unit = SpeedUnit.MS;
    }

    public void accelerate(Speed delta){
        if(delta.unit == this.speed.unit && delta.value > 0) {
            this.speed.value += delta.value;
        }
    }
}
In [ ]:
class LandVehicle {
    private Speed speed;

    public LandVehicle(){
        this.speed = new Speed();
        this.speed.unit = SpeedUnit.KMH;
    }

    public void accelerate(Speed delta){
        if(delta.unit == this.speed.unit) {
            this.speed.value += delta.value;
        }
    }
}
In [ ]:
class Speed {
    private double value;
    private SpeedUnit unit;

    public Speed(double value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
    
    public Speed add(Speed that){
        if(this.unit == that.unit){
            return new Speed(this.value + that.value, this.unit)
        } else {
            //...
        }
    }
    
    public boolean isAboveZero(){
        return value > 0;
    }
}
In [ ]:
class SpaceShip {
    private Speed speed;

    public SpaceShip(){
        this.speed = new Speed(0, SpeedUnit.MS);
    }

    public void accelerate(Speed delta){
        if(delta.isAboveZero()) {
            this.speed = this.speed.add(delta);
        }
    }
}
In [ ]:
class LandVehicle {
    private Speed speed;

    public SpaceShip(){
        this.speed = new Speed(0, SpeedUnit.KMH);
    }

    public void accelerate(Speed delta){
        this.speed = this.speed.add(delta);
    }
}

Modyfikacja zachowania w klasie dziedziczącej

In [ ]:
class Speed {
    private double value;
    private SpeedUnit unit;
    
    public Speed(double value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
    
    @Override
    public String toString(){
        return "" + value + " " + unit;
    }
}
In [ ]:
Speed speed1 = new Speed(10, SpeedUnit.KMH);
speed1.toString();
In [ ]:
Object speed2 = new Speed(10, SpeedUnit.KMH);
speed2.toString();

Rozszerzenie zachowania klasy nadrzędnej

In [ ]:
class Speed {
    private double value;
    private SpeedUnit unit;

    public Speed(double value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }

    @Override
    public String toString(){
        return super.toString() + " " + value + " " + unit;
    }
}
In [ ]:
Speed speed1 = new Speed(10, SpeedUnit.KMH);
speed1.toString();

Pola vs. metody

In [ ]:
enum SpeedUnit {
    MS, KMH, MA
}
In [ ]:
class Speed {
    public double value;
    public SpeedUnit unit;

    public Speed(double value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
In [ ]:
class SuperSonicSpeed extends Speed {
    public double value;
    public SpeedUnit unit = SpeedUnit.MA;

    public SuperSonicSpeed(double value){
        super(value, SpeedUnit.MS);
    }
}
In [ ]:
Speed speed1 = new Speed(10, SpeedUnit.MS);
speed1.unit;
In [ ]:
Speed speed2 = new SuperSonicSpeed(10);
speed2.unit;
In [ ]:
SuperSonicSpeed speed3 = new SuperSonicSpeed(10);
speed3.unit;

Modyfikator private

In [ ]:
class Speed {
    private static final double MS_TO_KMH_RATIO = 3.6;
 
    public Speed add(Speed that){
        return new Speed(this.value + that.convert(this.unit), this.unit);
    }
    
    public Speed subtract(Speed that){
        return new Speed(this.value - that.convert(this.unit), this.unit);
    }

    private double convert(SpeedUnit target){
        if(this.unit == SpeedUnit.KMH && target == SpeedUnit.MS){
            return this.value / MS_TO_KMH_RATIO;
        } else if(this.unit == SpeedUnit.MS && target == SpeedUnit.KMH){
            return this.value * MS_TO_KMH_RATIO;
        } else if(...) {
            // pozostałe przypadki
        } else {
            return this.value;
        }
    }   
}
In [ ]:
Speed speed1 = new Speed(10, SpeedUnit.MS);
Speed speed2 = new Speed(36, SpeedUnit.KMH);
speed1.add(speed2);
In [ ]:
speed1.convert(SpeedUnit.MS);     // niedozwolone

In [ ]:
class SuperSonicSpeed extends Speed {
    public SuperSonicSpeed(double value){
        super(value, SpeedUnit.MA);
    }

    private double convert(SpeedUnit target){
        return 0;
    }
}
In [ ]:
SuperSonicSpeed speed1 = new SuperSonicSpeed(10);
SuperSonicSpeed speed2 = new SuperSonicSpeed(10);
speed1.add(speed2);

Modyfikator protected

In [ ]:
class Speed {
    protected double convert(SpeedUnit target){
        if(this.unit == SpeedUnit.KMH && target == SpeedUnit.MS){
            return this.value / MS_TO_KMH_RATIO;
        } else if(this.unit == SpeedUnit.MS && target == SpeedUnit.KMH){
            return this.value * MS_TO_KMH_RATIO;
        } else {
            return this.value;
        }
    }   
}
In [ ]:
class SuperSonicSpeed extends Speed{
    public SuperSonicSpeed(double value){
        super(value, SpeedUnit.MA);
    }

    protected double convert(SpeedUnit target){
        return 0;
    }
}
In [ ]:
SuperSonicSpeed speed1 = new SuperSonicSpeed(10);
SuperSonicSpeed speed2 = new SuperSonicSpeed(10);
speed1.add(speed2);

Modyfikator final

In [ ]:
class Speed {
    private final double convert(SpeedUnit target){
        if(this.unit == SpeedUnit.KMH && target == SpeedUnit.MS){
            return this.value / MS_TO_KMH_RATIO;
        } else if(this.unit == SpeedUnit.MS && target == SpeedUnit.KMH){
            return this.value * MS_TO_KMH_RATIO;
        } else {
            return this.value;
        }
    }   
}
In [ ]:
class SuperSonicSpeed extends Speed{
    public SuperSonicSpeed(double value){
        super(value, SpeedUnit.MA);
    }

    //niedozwolone
    private double convert(SpeedUnit target){
        return 0;
    }
}

Modyfikator static

In [ ]:
class Speed {
    private double value;
    
    private static Map<Double, Speed> instances =
        new HashMap<>();

    private Speed(double value){
        this.value = value;
    }

    public static Speed create(double value){
        if(instances.containsKey(value)){
            return instances.get(value);
        } else {
            Speed instance = new Speed(value);
            instances.put(value, instance);
            return instance;
        }
    }
}
In [ ]:
Speed speed1 = Speed.create(10);
Speed speed2 = Speed.create(10);

speed1 == speed2

Dziedziczenie a metody statyczne

In [ ]:
class Speed {
    private double value;

    private static Map<Double, Speed> instances =
        new HashMap<>();

    private Speed(double value){
        this.value = value;
    }

    public static Speed create(double value){
        if(instances.containsKey(value)){
            return instances.get(value);
        } else {
            Speed instance = createInstance(value);
            instances.put(value, instance);
            return instance;
        }
    }
    
    protected static Speed createInstance(double value){
        return new Speed(value);
    }
}
In [ ]:
class SuperSonicSpeed extends Speed {
    private SuperSonicSpeed(double value){
        super(value);
    }
    
    protected static SuperSonicSpeed createInstance(double value){
        return new SuperSonicSpeed(value * 10);
    }
}
In [ ]:
SuperSonicSpeed speed = SuperSonicSpeed.create(10);

Antywzorzec

In [ ]:
class Speed {
    public static Speed toMs(Speed speed1){
        return new Speed(speed1.convert(SpeedUnit.MS), SpeedUnit.MS);
    }
}

Speed speed1 = new Speed(10, SpeedUnit.MS);
Speed.toMs(speed1);

// vs.

speed1.toMs();

Modyfikator abstract

In [ ]:
abstract class AbstractSpaceShip {
    private Speed speed;
    
    public AbstractSpaceShip(){
        this.speed = new Speed(0, SpeedUnit.MS);
    }
    
    public abstract void start();
    
    public abstract void land();
}
In [ ]:
class Rocket extends AbstractSpaceShip {
    private Engine engine;
    private Parachute parachute;

    public void start(){
        engine.start();
    }
    
    public void land(){
        parachute.open();
    }
}
In [ ]:
class SpaceShuttle extends AbstractSpaceShip {
    private List<Booster> boosters;
    
    public void start(){
        for(Booster booster : boosters){
            booster.start();
        }
    }
    
    public void land(){
        descend(new Distance(10, DistanceUnit.KM));
    }
    
    private void descend(Distance distance){
        //...
    }
}

In [ ]:
// aliens are coming... 

List<AbstractSpaceShip> spaceFleet = new LinkedList<>();
spaceFleet.add(new Rocket());
spaceFleet.add(new SpaceShuttle());

for(AbstractSpaceShip ship : spaceFleet){
    ship.start();
}

// rescue the World, then...

for(AbstractSpaceShip ship : spaceFleet){
    ship.land();
}

Interfejs (aka międzygębie)

In [ ]:
interface ISpaceShip {
    void start();
    void land();
}
In [ ]:
class Rocket implements ISpaceShip {
    public void start(){
        //...
    }
    
    public void land(){
        //...
    }
}
In [ ]:
class SpaceShuttle implements ISpaceShip {
    public void start(){
        //...
    }

    public void land(){
        //...
    }
}
In [ ]:
// aliens are coming... 

List<ISpaceShip> spaceFleet = new LinkedList<>();
spaceFleet.add(new Rocket());
spaceFleet.add(new SpaceShuttle());

for(AbstractSpaceShip ship : spaceFleet){
    ship.start();
}

// rescue the World, then...

for(AbstractSpaceShip ship : spaceFleet){
    ship.land();
}
In [ ]:
interface ISpaceShip {
    void start();
    void land();
    // nowa metoda
    void launchMissile();
}

Modyfikator default

In [ ]:
interface ISpaceShip {
    void start();
    void land();
    
    // nowość w Javie 8.0
    default void launchMissile(){
        InterplanetarySystem.out.
            println("Włamujemy się emacsem przez sendmeila");
        InterplanetarySystem.out.
            println("omijając potrójną ścianę ogniową");
    }
}

Dziedziczenie vs. kompozycja

In [ ]:
abstract class Car {
    public void openDoor(DoorSpecification doorSpec){
        doors.get(doorSpec).open();
    }
    
    public abstract void drive();
}
In [ ]:
class GasolineCar extends Car {
    private GasolineEngine engine;
    
    public void drive(){
        engine.deliverGasoline();
        engine.igniteGasoline();
        //...
    }
}
In [ ]:
class ElectricCar extends Car {
    private List<WheelMotor> motors;
    
    public void drive(){
        for(WheelMotor motor : motors){
            motor.rotate();
        }
    }
}
In [ ]:
class Car {
    private IDriveSystem driveSystem;
    
    public Car(IDriveSystem driveSystem){
        this.driveSystem = driveSystem;
    }
    
    public drive(){
        this.driveSystem.drive();
    }
}
In [ ]:
interface IDriveSystem {
    void drive();
}

class ElectricDriveSystem implements IDriveSystem {
    //...
}

class GasolineDriveSystem implements IDriveSystem {
    //...
}
In [ ]:
Car car = new Car(new GasolineDriveSystem());