Sposoby definiowania zachowania się 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 zachowania

class Speed {
    private final double value;
    private final 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;
    }
}
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);
        }
    }
}

Modyfikacja zachowania w klasie dziedziczącej

class Speed {
    private final double value;
    private final SpeedUnit unit;

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

    @Override
    public String toString(){
        return "" + value + " " + unit;
    }
}
Speed speed1 = new Speed(10, SpeedUnit.KMH);
speed1.toString();
Object speed2 = new Speed(10, SpeedUnit.KMH);
speed2.toString();

Rozszerzenie zachowania klasy nadrzędnej

class Speed {
    private final double value;
    private final SpeedUnit unit;

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

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

Pola vs. metody

enum SpeedUnit {
    MS, KMH, MA
}
class Speed {
    public double value;
    public SpeedUnit unit;

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

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

Modyfikator private

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 {
            return this.value;
        }
    }   
}
Speed speed1 = new Speed(10, SpeedUnit.MS);
Speed speed2 = new Speed(36, SpeedUnit.KMH);
speed1.add(speed2);
speed1.convert(SpeedUnit.MS);     // niedozwolone

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

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

Modyfikator protected

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;
        }
    }   
}
class SuperSonicSpeed extends Speed{
    public SuperSonicSpeed(double value){
        super(value, SpeedUnit.MA);
    }

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

Modyfikator final

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;
        }
    }   
}
class SuperSonicSpeed extends Speed{
    public SuperSonicSpeed(double value){
        super(value, SpeedUnit.MA);
    }

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

Modyfikator static

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;
        }
    }
}
Speed speed1 = Speed.create(10);
Speed speed2 = Speed.create(10);

speed1 == speed2

Dziedziczenie a metody statyczne

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);
    }
}
class SuperSonicSpeed extends Speed {
    private SuperSonicSpeed(double value){
        super(value);
    }

    protected static SuperSonicSpeed createInstance(double value){
        return new SuperSonicSpeed(value * 10);
    }
}
SuperSonicSpeed speed = SuperSonicSpeed.create(10);

Antywzorzec

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

abstract class AbstractSpaceShip {
    private Speed speed;

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

    public abstract void start();

    public abstract void land();
}
class Rocket extends AbstractSpaceShip {
    private Engine engine;
    private Parachute parachute;

    public void start(){
        engine.start();
    }

    public void land(){
        parachute.open();
    }
}
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){
        //...
    }
}

// aliens are coming... 

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

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

// rescue then World, then...

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

Interfejs

interface ISpaceShip {
    void start();
    void land();
}
class Rocket implements ISpaceShip {
    public void start(){
        //...
    }

    public void land(){
        //...
    }
}
class SpaceShuttle implements ISpaceShip {
    public void start(){
        //...
    }

    public void land(){
        //...
    }
}
// aliens are coming... 

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

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

// rescue then World, then...

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

Modyfikator default

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

abstract class Car {
    public void openDoor(DoorSpecification doorSpec){
        doors.get(doorSpec).open();
    }

    public abstract void drive();
}
class GasolineCar extends Car {
    private GasolineEngine engine;

    public void drive(){
        engine.deliverGasoline();
        engine.igniteGasoline();
        //...
    }
}
class ElectricCar extends Car {
    private List<WheelMotor> motors;

    public void drive(){
        for(WheelMotor motor : motors){
            motor.rotate();
        }
    }
}
class Car {
    private IDriveSystem driveSystem;

    public Car(IDriveSystem driveSystem){
        this.driveSystem = driveSystem;
    }

    public drive(){
        this.driveSystem.drive();
    }
}
interface IDriveSystem {
    void drive();
}

class ElectricDriveSystem implements IDriveSystem {
    //...
}

class GasolineDriveSystem implements IDriveSystem {
    //...
}
Car car = new Car(new GasolineDriveSystem());

Pytania?