Stan w programowaniu obiektowym

Zmienne instancyjne i klasowe, zasady dostępu do danych


dr inż. Aleksander Smywiński-Pohl

apohllo@o2.pl

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

Sonda Mars Climate Orbiter

Program strukturalny

struct SpaceProbe {
    double spaceProbeSpeed;
    double spaceProbeWeight;
}

double thrustCorrectionForX(struct SpaceProbe spaceProbe){
    // operowanie na polach spaceProbeSpeed oraz spaceProbeWeight
}

double thrustCorrectionForY(struct SpaceProbe spaceProbe){
    // ...
}

double thrustCorrectionForZ(struct SpaceProbe spaceProbe){
    // ...
}

Primitive obsession

class SpaceProbe {
    private double speedX;
    private double speedY;
    private double speedZ;
    private double weight;

    public double thrustCorrectionForX(double positionX, 
                      double positionY, double positionZ){
        //...
    }
}

Klasa Speed

class Speed {
    public int value;
    public String unit;

    public Speed(int value, String unit){
        this.value = value;
        this.unit = unit;
    }
}
Speed speed1 = new Speed(10, "km/h");
Speed speed2 = new Speed(20, "m/s");

speed1.value = 20;
speed2.unit = "km/h";

speed1.value = -10;
speed2.unit = "ala ma kota";

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

    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
Speed speed1 = new Speed(10, SpeedUnit.KMH);
Speed speed2 = new Speed(20, SpeedUnit.MS);

Modyfikator private

class SpaceProbe {
    private Speed speed;

    public SpaceProbe(Speed speed){
        this.speed = speed;
    }

    public getSpeed(){
        return this.speed;
    }
}
SpaceProbe probe1 = new SpaceProbe(new Speed(10, SpeedUnit.KMH));

probe1.speed = new Speed(10, SpeedUnit.MS); // niedozwolone, pole jest prywatne!
Speed speed1 = probe1.getSpeed();

speed1.unit = SpeedUnit.MS;         // zmienia się jednostka prędkości sondy!

Modyfikator final - Speed jako ValueObject

class Speed {
    public final int value;
    public final SpeedUnit unit;

    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
SpaceProbe probe1 = newSpaceProbe(new Speed(10, SpeedUnit.KMH));
Speed speed1 = probe1.getSpeed();

speed1.unit = SpeedUnit.MS;                 // niedozwolone!!!

Modyfikator private

class Speed {
    private int value;
    private SpeedUnit unit;

    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
class SpaceProbe
    private Speed speed;

    public void accelerate(Speed delta){
        speed.value += delta.value;                              // niedozwolone!
    }
}
class Speed {
    public Speed add(Speed that){
        if(this.unit == that.unit){
            return new Speed(this.value + that.value, this.unit);
        } else {
            return null;
        }
    }
}
class SpaceProbe
    private Speed speed;

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

Modyfikator protected

class Speed {
    protected SpeedUnit unit;

    public Speed(SpeedUnit unit){
        this.unit = unit;
    }
}
class Speed1D extends Speed {
    protected int value;

    public Speed1D(int value, SpeedUnit unit){
        super(unit);
        this.value = value
    }

    public Speed1D add(Speed1D delta){
        if(this.unit == delta.unit){
            return new Speed1D(this.value + delta.value, this.unit);
        } else {
            return null;
        }
    }
}
Speed1D speed1 = new Speed1D(10, SpeedUnit.KMH);
Speed1D speed2 = new Speed1D(20, SpeedUnit.KMH);

Speed1D speed3 = speed1.add(speed2);
class Speed3D extends Speed {
    protected int valueX;
    protected int valueY;
    protected int valueZ;

    public Speed3D(int x, int y, int z, SpeedUnit unit){
        super(value);
        this.valueX = x;
        this.valueY = y;
        this.valueZ = z;
    }

    public Speed3D add(Speed3D delta){
        if(this.unit == delta.unit){
            return new Speed3D(this.valueX + delta.valueX, 
                this.valueY + delta.valueY,
                this.valueZ + delta.valueZ, 
                this.unit);
        } else {
            return null;
        }
    }
}

Dostęp pakietowy

package agh.cs.lecture;

class Speed {
    int value;
    SpeedUnit unit;

    public Speed(int value, SpeedUnit unit){
        this.value = value;
        this.unit = unit;
    }
}
package agh.cs.lecture;

class SpaceProbe {
    private Speed speed;

    public void accelerate(Speed delta){
        if(this.speed.unit == delta.unit){           // dozwolone
            //..
        }
    }
}
package com.mycompany;

class SpaceShip {
    private Speed speed;

    public void accelerate(Speed delta){
        if(this.speed.unit == delta.unit){           // niedozwolone!
            //..
        }
    }
}

Metody dostępowe - "gettery"

class Speed {
    private int value;
    private SpeedUnit unit;

    public int getValue(){
        return this.value;
    }

    public SpeedUnit getUnit(){
        return this.unit;
    }
}

Dostęp dziedzinowy

class Speed {
    private int value;
    private SpeedUnit unit;

    public int getValueInMS(){
        if(this.unit == SpeedUnit.MS){
            return value;
        } else {
            return convert(this.unit, SpeedUnit.MS, this.value);
        }
    }

    public int getValueInKMH(){
        //...
    }

    private int convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.KMS && to == SpeedUnit.MS)
            return Math.round(value / 3.6);
        } else if(...){
            //...

        }
    }
}

Metody dostępowe - "settery"

class Speed {
    private int value;
    private SpeedUnit unit;

    public setValue(int value){
        this.value = value;
    }

    public setUnit(SpeedUnit unit){
        this.value = convert(this.unit, unit, this.value);
        this.unit = unit;
    }
}

Utrzymywanie jednolitej reprezentacji

class Speed {
    private int valueInMs;
    private SpeedUnit unit;

    public Speed(int value, SpeedUnit unit){
        this.valueInMs = convert(unit, SpeedUnit.MS, value);
        this.unit = unit;
    }

    public getValue(){
        return convert(SpeedUnit.MS, this.unit, this.valueInMs);
    }

    public setValue(int value){
        this.valueInMs = convert(unit, SpeedUnit.MS, value);
    }

    public setUnit(SpeedUnit unit){
        this.unit = unit;
    }
}

ValueObject - konwersja

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

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

    public Speed convertToMs(){
        if(this.unit == SpeedUnit.MS){
            return this;
        } else {
            return new Speed(convert(this.unit, SpeedUnit.MS, this.value), 
                             SpeedUnit.MS);
        }
    }
}

Shadowing

enum SpeedUnit { MS, KMH }
class Speed {
    private SpeedUnit unit;

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

    public SpeedUnit getSuperUnit(){
        return this.unit;
    }

    public SpeedUnit getUnit(){
        return this.unit;
    }
}
class Speed1D extends Speed{
    private SpeedUnit unit;
    private int value;

    public Speed1D(int value, SpeedUnit unit){
        super(unit);
        this.value = value;
        this.unit = SpeedUnit.KMH;
    }

    public SpeedUnit getUnit(){
        return this.unit;
    }
}
Speed1D speed1 = new Speed1D(10, SpeedUnit.MS);
speed1.getUnit();
speed1.getSuperUnit();

Zmienne statyczne

class Speed {
    private int value;
    private SpeedUnit unit;

    private int convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.MS && to == SpeedUnit.KMH){
            return value * 3.6;
        } else if(from == SpeedUnit.KMH && to == SpeedUnit.MS){
            return value / 3.6;
        } else {
            //...
        }
    }
}
class Speed {
    private final double ms2kmhRatio = 3.6;

    private int value;
    private SpeedUnit unit;

    private int convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.MS && to == SpeedUnit.KMH){
            return value * ms2kmhRatio;
        } else if(from == SpeedUnit.KMH && to == SpeedUnit.MS){
            return value / ms2kmhRatio;
        } else {
            //...
        }
    }
}

Modyfikator static

class Speed {
    private static final double ms2kmhRatio = 3.6;

    private int value;
    private SpeedUnit unit;

    private int convert(SpeedUnit from, SpeedUnit to, int value){
        if(from == SpeedUnit.MS && to == SpeedUnit.KMH){
            return value * ms2kmhRatio;
        } else if(from == SpeedUnit.KMH && to == SpeedUnit.MS){
            return value / ms2kmhRatio;
        } else {
            //...
        }
    }
}

Antywzorzec

class SpaceShip {
    public static List<SpaceShip> ships = new LinkedList<>();

    public SpaceShip(){
        ships.add(this);
    }
}
class Space() {
    private List<SpaceShip> ships = new LinkedList<>();

    public void add(SpaceShip ship){
        ships.add(ship);
    }
}

class SpaceShip {
    public SpaceShip(Space space){
        space.add(this);
    }
}

Model prywatności w Ruby

class Speed
  def initialize(value, unit)
    @value = value
    @unit = unit
  end

  def to_s
    "#{@value} #{@unit}"
  end

  def +(other)
    Speed.new(@value + other.@value, @unit + other.@unit) # niepoprawna składnia!
  end
end

class Speed
  attr_reader :unit, :value

  def initialize(value, unit)
    @value = value
    @unit = unit
  end

  def +(other)
    Speed.new(@value + other.value, @unit + other.unit)
  end
end

class SpaceShip
  def initalize(speed)
    @speed = speed
  end

  def accelerate(speed)
    @speed += speed
  end
end
class Speed
  attr_reader :unit

  def initialize(unit)
    @unit = unit
  end
end
class Speed1D
  attr_reader :value

  def initialize(unit, value)
    super(unit)
    @value = value
    @unit = :KMH
  end
end
speed1 = Speed1D.new(10, :MS)
speed1.unit #=> :KMH

Pytania?