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

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

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

Transcript

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

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

    обектно-ориентирания дизайн
  3. Как започва всичко?

  4. Simula Оле Дал Кристен Нигаард Class Glyph; Virtual: Procedure print

    Is Procedure print; Begin End; Glyph Class Char (c); Character c; Begin Procedure print; OutChar(c); End;
  5. Smalltalk Алън Кей |myArray myOperation| myArray := #('A' 'b' 'b'

    'a'). myOperation := [:each | Transcript show: each. Transcript cr. ]. myArray do: myOperation.
  6. Принципи •Всичко е обект •Обектите си комуникират със съобщения •Обектите

    не знаят какво е вътрешното устройство на другите обекти 1.class #=> Fixnum player.play(song) transaction.finish # def finish # @ammendable = false # ... # end
  7. None
  8. Perl Лари Уол $user = @users[0]

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

  10. Обектно-ориентиран дизайн S O L I D ingle responsibility pen

    for extension iskov substitution nterface segregation ependency inversion на теория
  11. Обектно-ориентиран дизайн на практика

  12. None
  13. None
  14. Голямата тайна на занаята Проблемите не се решават с код

    на Java, C++ или Ruby, а със специфичен за областта на проблема език, създаден с Java, C++ или Ruby, на който пишем код, който решава проблема.
  15. Днес, част 2 • Какво научих за 3 месеца писане

    на домашни по Ruby? • Пишете добре дизайннат код • POODR by Sandy Metz • А вие всъщност... ... карате ли колело?
  16. Single • Желаем ли да преизползваме част от класа, то

    той няма single responsibility • Или да си поговорим за колела, а? • Знаете ли как работят скоростите? • ratio = chainring / cog.to_f • gear_inches = ratio * (rim + (tire *
  17. 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? • А как бихме го описали в едно изречение? Изречението съдържа
  18. Да си многознайко 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 знае твърде много!
  19. По-простичък = по- 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
  20. 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 с едно изречение? • Трябва ли ни да намираме диаметър на точно едно колело?
  21. Зависимости • Обект зависи от друг обект, ако промяната на

    един обект налага промяната на друг • Ако няколко обекта са силно зависими, те работят като цяло и не могат да бъдат преизползвани поотделно
  22. Зависимости и 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
  23. Изолиране на 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 # ...
  24. Изолиране на външни 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
  25. Патешки истории • Патиците са обекти, дефинирани от поведеното си,

    а не от класа си • Ако един обект знае типа на друг, то знае и на какви съобщения може да отговори • Не е важно какъв е един обект, а какво прави
  26. Хипотетичен пример • Имаме компания, която прави планински и градски

    обиколки с колела • Всяка обиколка следва определен маршрут, който има определена сложност • Клиентите могат да наемат колело за съответната обиколка • Колелата, запазени за всяка обиколка, трябва да са проверени и подготвени преди обиколката (напомпани гуми, проверени спирачки, скорости, и т.н.). За
  27. Хипотетичен код 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 метода няма експлицитна зависимост от класа механик • Да си представим, обаче, че нещата станат малко по-сложни
  28. Хипотетично усложнение • Освен механик, вече имаме нужда от координатор

    и шофьор • Координаторът осигурява необходимите неща за трипа, например храна, вода, нощувки и т.н. • Шофьорът трябва да се движи близо до основната група, отговорен е за багажа и е важен при инциденти • Всеки от тримата трябва да се подготви преди обиколката
  29. Хипотетично усложнен # Подготвянето на трипа става по-сложно 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
  30. Да намерим патките • #prepare методът на Trip има само

    една цел, трябва да подготви обиколката • Трябва да се абстрахираме от знанието ни за класовете и техните методи • Аргументите на prepare могат да си вършат работата, ако #prepare им вярва повече
  31. Хипотетично опростен # Подготвянето на трипа става по-лесно 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
  32. Къде се крият патките 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
  33. Бележки някакви • Кода от лекцията е тук • Карайте

    колело (: • Край.