Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Обектно-ориентирано програмиране - произход и основи

Обектно-ориентирано програмиране - произход и основи

Лекция от курса "Програмиране с Ruby", воден 2015/16 уч. година във ФМИ към СУ.

More Decks by Programming Ruby @ fmi.uni-sofia.bg

Other Decks in Programming

Transcript

  1. Обектно-ориентирано
    програмиране
    Произход и основи

    View full-size slide

  2. Днес
    •История на обектно-ориентирания подход
    •Произходът на Ruby
    •Есенция на обектно-ориентирания дизайн

    View full-size slide

  3. Как започва всичко?

    View full-size slide

  4. Simula
    Оле Дал Кристен Нигаард
    Class Glyph;
    Virtual: Procedure print Is Procedure print;
    Begin
    End;
    Glyph Class Char (c);
    Character c;
    Begin
    Procedure print;
    OutChar(c);
    End;

    View full-size slide

  5. Smalltalk
    Алън Кей
    |myArray myOperation|
    myArray := #('A' 'b' 'b' 'a').
    myOperation := [:each |
    Transcript show: each.
    Transcript cr.
    ].
    myArray do: myOperation.

    View full-size slide

  6. Принципи
    •Всичко е обект
    •Обектите си комуникират със съобщения
    •Обектите не знаят какво е вътрешното
    устройство на другите обекти
    1.class #=> Fixnum
    player.play(song)
    transaction.finish
    # def finish
    # @ammendable = false
    # ...
    # end

    View full-size slide

  7. Perl
    Лари Уол
    $user = @users[0]

    View full-size slide

  8. Ruby
    Юкихиро
    Мацумото

    View full-size slide

  9. Обектно-ориентиран
    дизайн
    S
    O
    L
    I
    D
    ingle responsibility
    pen for extension
    iskov substitution
    nterface segregation
    ependency inversion
    на теория

    View full-size slide

  10. Обектно-ориентиран
    дизайн
    на практика

    View full-size slide

  11. Голямата
    тайна
    на занаята
    Проблемите не се решават с код на Java, C++ или
    Ruby, а със специфичен за областта на проблема език,
    създаден с Java, C++ или Ruby, на който пишем код,
    който решава проблема.

    View full-size slide

  12. Днес, част 2
    • Какво научих за 3 месеца
    писане на домашни по Ruby?
    • Пишете добре дизайннат код
    • POODR by Sandy Metz
    • А вие всъщност...
    ... карате ли колело?

    View full-size slide

  13. Single
    • Желаем ли да преизползваме част
    от класа, то той няма single
    responsibility
    • Или да си поговорим за колела, а?
    • Знаете ли как работят скоростите?
    • ratio = chainring / cog.to_f
    • gear_inches = ratio * (rim + (tire *

    View full-size slide

  14. Single
    class Gear
    attr_reader :chainring, :cog, :rim, :tire
    def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
    end
    def ratio
    chainring / cog.to_f # добра практика е да използваме accessor методи
    end
    def gear_inches
    ratio * (rim + (tire * 2))
    end
    end
    • На какви съобщения отговаря
    Gear?
    • А как бихме го описали в едно
    изречение? Изречението съдържа

    View full-size slide

  15. Да си многознайко
    class ObscuringReferences
    attr_reader :data
    def initialize(data)
    @data = data
    end
    def diameters
    #diameter знае, че 0 е rim, 1 е tire
    data.collect {|cell|
    cell[0] + (cell[1] * 2)}
    end
    end
    @data = [[622, 20], [622, 23], [559, 30], [559, 40]] # Списък от списъци
    • diameters знае твърде много!

    View full-size slide

  16. По-простичък = по-
    class RevealingReferences
    attr_reader :wheels
    def initialize(data)
    @wheels = wheelify(data)
    end
    def diameters
    wheels.collect {|wheel|
    wheel.rim + (wheel.tire * 2)}
    end
    Wheel = Struct.new(:rim, :tire) # всеки може да праща rim/tire на wheel
    def wheelify(data)
    data.collect {|cell|
    Wheel.new(cell[0], cell[1])}
    end
    end
    • Всичко, което diameters знае е, че
    wheels връща enumerable
    • Ако списъкът се промени, трябва
    да променим само wheelify

    View full-size slide

  17. Single Responsibility…
    def diameters
    wheels.collect {|wheel|
    wheel.rim + (wheel.tire * 2)}
    еnd
    def diameters # първо итерираме
    wheels.collect {|wheel| diameter(wheel)}
    еnd
    def diameter(wheel) # после калкулираме за едно колело
    wheel.rim + (wheel.tire * 2)
    end
    • Как да опишем diameters с едно
    изречение?
    • Трябва ли ни да намираме
    диаметър на точно едно колело?

    View full-size slide

  18. Зависимости
    • Обект зависи от друг обект, ако
    промяната на един обект налага
    промяната на друг
    • Ако няколко обекта са силно
    зависими, те работят като цяло и
    не могат да бъдат преизползвани
    поотделно

    View full-size slide

  19. Зависимости и duck
    class Gear
    attr_reader :chainring, :cog, :rim, :tire
    def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
    end
    def gear_inches
    ratio * Wheel.new(rim, tire).diameter
    end
    # ...
    end
    Gear.new(52, 11, 26, 1.5).gear_inches
    • Gear знае за съществуването на
    Wheel, какви проблеми може да
    възникнат?
    class Gear
    attr_reader :chainring, :cog, :wheel
    def initialize(chainring, cog, wheel)
    @chainring = chainring
    @cog = cog
    @wheel = wheel
    end
    def gear_inches
    ratio * wheel.diameter
    end
    # ...
    end
    # Gear очаква „патка“, която знае'diameter'
    Gear.new(52,11,Wheel.new(26,1.5)).gear_inches

    View full-size slide

  20. Изолиране на
    class Gear
    attr_reader :chainring, :cog, :rim,
    :tire, :wheel
    def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @wheel = Wheel.new(rim, tire)
    end
    def gear_inches
    ratio * wheel.diameter
    end
    # ...
    • Два начина за изолиране на
    зависимости
    class Gear
    attr_reader :chainring, :cog, :rim,
    :tire, :wheel
    def initialize(chainring, cog, rim, tire)
    @chainring = chainring
    @cog = cog
    @rim = rim
    @tire = tire
    end
    def gear_inches
    ratio * wheel.diameter
    end
    def wheel
    @wheel ||= Wheel.new(rim, tire)
    end
    # ...

    View full-size slide

  21. Изолиране на външни
    def gear_inches
    #... Няколко реда сложна математика
    foo = some_intermediate_result *
    wheel.diameter
    #... Още редове сложна математика
    end
    • Външни съобщения – такива
    изпратени на друг (не на self)
    def gear_inches
    #... Няколко реда сложна математика
    foo = some_intermediate_result * diameter
    #... Още редове сложна математика
    end
    #...
    def diameter
    wheel.diameter
    end

    View full-size slide

  22. Патешки истории
    • Патиците са обекти, дефинирани от
    поведеното си, а не от класа си
    • Ако един обект знае типа на друг, то
    знае и на какви съобщения може да
    отговори
    • Не е важно какъв е един обект, а какво
    прави

    View full-size slide

  23. Хипотетичен пример
    • Имаме компания, която прави планински
    и градски обиколки с колела
    • Всяка обиколка следва определен
    маршрут, който има определена
    сложност
    • Клиентите могат да наемат колело за
    съответната обиколка
    • Колелата, запазени за всяка обиколка,
    трябва да са проверени и подготвени
    преди обиколката (напомпани гуми,
    проверени спирачки, скорости, и т.н.). За

    View full-size slide

  24. Хипотетичен код
    class Trip
    attr_reader :bicycles, :customers, :vehicle
    # аргументът 'mechanic' може да бъде от всякакъв клас
    def prepare(mechanic)
    mechanic.prepare_bicycles(bicycles)
    end
    # ...
    end
    # ако подадем инстанция на класа долу, ще работи
    class Mechanic
    def prepare_bicycles(bicycles)
    bicycles.each {|bicycle| prepare_bicycle(bicycle)}
    end
    def prepare_bicycle(bicycle)
    #...
    end
    end
    • prepare метода няма експлицитна
    зависимост от класа механик
    • Да си представим, обаче, че
    нещата станат малко по-сложни

    View full-size slide

  25. Хипотетично усложнение
    • Освен механик, вече имаме нужда от
    координатор и шофьор
    • Координаторът осигурява необходимите
    неща за трипа, например храна, вода,
    нощувки и т.н.
    • Шофьорът трябва да се движи близо до
    основната група, отговорен е за багажа и
    е важен при инциденти
    • Всеки от тримата трябва да се подготви
    преди обиколката

    View full-size slide

  26. Хипотетично усложнен
    # Подготвянето на трипа става по-сложно
    class Trip
    attr_reader :bicycles, :customers, :vehicle
    def prepare(preparers)
    preparers.each do |preparer|
    case preparer
    when Mechanic
    preparer.prepare_bicycles(bicycles)
    when TripCoordinator
    preparer.buy_food(customers)
    when Driver
    preparer.gas_up(vehicle)
    preparer.fill_water_tank(vehicle)
    end
    end
    end
    end
    • Какви проблеми виждате?
    • Трябва ни конкретен клас за
    конкретно съобщение с конкретен
    аргумент
    • А ако се в бъдеще ни трябват още
    # когато вкараме TripCoordinator и Driver
    class TripCoordinator
    def buy_food(customers)
    # ...
    end
    end
    class Driver
    def gas_up(vehicle)
    #...
    end
    def fill_water_tank(vehicle)
    #...
    end
    end

    View full-size slide

  27. Да намерим патките
    • #prepare методът на Trip има само една
    цел, трябва да подготви обиколката
    • Трябва да се абстрахираме от знанието
    ни за класовете и техните методи
    • Аргументите на prepare могат да си
    вършат работата, ако #prepare им вярва
    повече

    View full-size slide

  28. Хипотетично опростен
    # Подготвянето на трипа става по-лесно
    class Trip
    attr_reader :bicycles, :customers, :vehicle
    def prepare(preparers)
    preparers.each {|preparer|
    preparer.prepare_trip(self)}
    end
    end
    # когато всеки подготвител е „патка“
    # която отговаря на 'prepare_trip'
    class Mechanic
    def prepare_trip(trip)
    trip.bicycles.each {|bicycle|
    prepare_bicycle(bicycle)}
    # ...
    end
    class TripCoordinator
    def prepare_trip(trip)
    buy_food(trip.customers)
    end
    # ...
    end
    class Driver
    def prepare_trip(trip)
    vehicle = trip.vehicle
    gas_up(vehicle)
    fill_water_tank(vehicle)
    end
    # ...
    end

    View full-size slide

  29. Къде се крият патките
    if preparer.kind_of?(Mechanic)
    preparer.prepare_bicycles(bicycle)
    elsif preparer.kind_of?(TripCoordinator)
    preparer.buy_food(customers)
    elsif preparer.kind_of?(Driver)
    preparer.gas_up(vehicle)
    preparer.fill_water_tank(vehicle)
    end
    Патките се крият от хората с колела,
    търсете ги там, където има:
    • case, който switch-ва в зависимост от
    клас
    • kind_of? или is_a?
    if preparer.responds_to?(:prepare_bicycles)
    preparer.prepare_bicycles(bicycle)
    elsif preparer.responds_to?(:buy_food)
    preparer.buy_food(customers)
    elsif preparer.responds_to?(:gas_up)
    preparer.gas_up(vehicle)
    preparer.fill_water_tank(vehicle)
    end

    View full-size slide

  30. Бележки някакви
    • Кода от лекцията е тук
    • Карайте колело (:
    • Край.

    View full-size slide