SlideShare a Scribd company logo
1 of 92
Дизайн ООП
правила хорошего тона. SOLID.




                                Щербинин Александр
                                undev.ru
Объектно ориентированное проектирование



    •   проектирование - суть программирования

    •   ваш код - проектная документация продукта

    •   задача проектирования - управление сложностью
        проекта
Ароматы дизайна
или как распознать загнивающий код
Ароматы дизайна
Ароматы дизайна
•   непрозрачность
Ароматы дизайна
•   непрозрачность

    -   сложность для понимания, запутанность
Ароматы дизайна
•   непрозрачность

    -   сложность для понимания, запутанность

•   жесткость
Ароматы дизайна
•   непрозрачность

    -   сложность для понимания, запутанность

•   жесткость

    -   сложность внесения изменений
Ароматы дизайна
•   непрозрачность

    -   сложность для понимания, запутанность

•   жесткость

    -   сложность внесения изменений

    -   простое изменение вызывает каскад зависимых
        правок
Ароматы дизайна
•   непрозрачность

    -   сложность для понимания, запутанность

•   жесткость

    -   сложность внесения изменений

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

•   хрупкость
Ароматы дизайна
•   непрозрачность

    -   сложность для понимания, запутанность

•   жесткость

    -   сложность внесения изменений

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

•   хрупкость

    -   исправление одной проблемы влечет появление
        множества новых
Ароматы дизайна
Ароматы дизайна

•   неподвижность
Ароматы дизайна

•   неподвижность

    -   сложно выделить части дизайна в отдельные
        компоненты
Ароматы дизайна

•   неподвижность

    -   сложно выделить части дизайна в отдельные
        компоненты

•   вязкость
Ароматы дизайна

•   неподвижность

    -   сложно выделить части дизайна в отдельные
        компоненты

•   вязкость

    -   сложно сохранить дизайн при изменении
Ароматы дизайна

•   неподвижность

    -   сложно выделить части дизайна в отдельные
        компоненты

•   вязкость

    -   сложно сохранить дизайн при изменении

    -   решить задачу правильно сложнее, чем сделать
        “хак”
Ароматы дизайна
Ароматы дизайна

•   ненужная сложность
Ароматы дизайна

•   ненужная сложность

    -   наблюдается overhead, наличие множества
        неиспользуемых элементов
Ароматы дизайна

•   ненужная сложность

    -   наблюдается overhead, наличие множества
        неиспользуемых элементов

    -   возникает, когда разработчик пытается
        предугадать последующие изменения
Ароматы дизайна

•   ненужная сложность

    -   наблюдается overhead, наличие множества
        неиспользуемых элементов

    -   возникает, когда разработчик пытается
        предугадать последующие изменения

•   ненужные повторения
Ароматы дизайна

•   ненужная сложность

    -   наблюдается overhead, наличие множества
        неиспользуемых элементов

    -   возникает, когда разработчик пытается
        предугадать последующие изменения

•   ненужные повторения

    -   один и тот же код с минимальными изменениями
        копируется снова и снова
SOLID
SOLID


•   Single responsibility principle

•   Open/closed principle

•   Liskov substitution principle

•   Interface segregation principle

•   Dependency inversion principle
Single responsibility principle
      Принцип единственной обязанности
SRP
Принцип единственной обязанности
SRP
      Принцип единственной обязанности


•   у класса должна быть только одна причина для
    изменения
SRP
      Принцип единственной обязанности


•   у класса должна быть только одна причина для
    изменения

•   то есть у класса должна быть только одна
    обязанность
SRP
      Принцип единственной обязанности


•   у класса должна быть только одна причина для
    изменения

•   то есть у класса должна быть только одна
    обязанность

•   сильное сцепление (high cohesion, см. GRASP)
SRP
       Принцип единственной обязанности

class Modem
  def dial(number)
  end                    1-я обязанность
  def hangup
                     управление соединением
  end

  def send(char)
  end
                        2-я обязанность
  def recv              передача данных
  end
end
SRP
               Принцип единственной обязанности
module ConnectionInterface
  def dial(number)
  end

  def hangup                   class Modem
  end                            include ConnectionInterface
end                              include DataChannelInterface
                               end
module DataChannelInterface
  def send(char)
  end

  def recv
  end
end
SRP
        Принцип единственной обязанности
                 class Modem
                   def dial(number)
                   end                     1-я обязанность
                                       управление соединением
                   def hangup
                   end

                   def send(char)
                   end                    2-я обязанность
                                          передача данных
                   def recv
                   end
                 end




Если необходимо изменить только процесс соединения - то дизайн
       становится жестким, и нужно разделять обязанности

Если изменяется одновременно и процесс соединения, и процесс
отправки - то разделение повлечет за собой излишнюю сложность
SRP
  Принцип единственной обязанности


class Basket
  def calculate      бизнес-логика
  end

  def store
  end              логика приложения
end
SRP
Принцип единственной обязанности
SRP
      Принцип единственной обязанности



•   не совмещайте бизнес-логику с логикой
    приложения
SRP
      Принцип единственной обязанности



•   не совмещайте бизнес-логику с логикой
    приложения

•   если вам очень-очень нужно совместить
    обязанности - не создавайте зависимостей
Open/closed principle
   Принцип открытости/закрытости
OCP
Принцип открытости/закрытости
OCP
        Принцип открытости/закрытости

•   программные сущности должны быть открыты для
    расширения, но закрыты для модификации
OCP
        Принцип открытости/закрытости

•   программные сущности должны быть открыты для
    расширения, но закрыты для модификации

•   открыты для расширения - мы можем изменить состав
    модуля, добавив новое поведение
OCP
        Принцип открытости/закрытости

•   программные сущности должны быть открыты для
    расширения, но закрыты для модификации

•   открыты для расширения - мы можем изменить состав
    модуля, добавив новое поведение

•   закрыты для модификации - расширение модуля не
    связано с изменением его исходного кода (уменьшаем
    жесткость)
OCP
       Принцип открытости/закрытости




Задача: необходимо рисовать геометрические фигуры
(круги и квадраты) в графическом интерфейсе.
OCP
            Принцип открытости/закрытости
             wrong
       (высокая жесткость)
Circle = Struct.new(:radius, :center)

# рисуем круг
def draw_circle
  #
end

Square = Struct.new(:side, :top_left)

# рисуем квадрат
def draw_square
  #
end

# рисуем список фигур
def draw_all_shapes(shapes)
  shapes.each do |shape|
    case shape.class
      when Circle; draw_circle(shape)
      when Square; draw_square(shape)
    end
  end
end
OCP
            Принцип открытости/закрытости
             wrong                                    better
       (высокая жесткость)
Circle = Struct.new(:radius, :center)         class Shape
                                                def draw
# рисуем круг                                     # interface
def draw_circle                                 end
  #                                           end
end
                                              class Circle < Shape
Square = Struct.new(:side, :top_left)           def draw
                                                  # implement
# рисуем квадрат                                end
def draw_square                               end
  #
end                                           class Square < Shape
                                                def draw
# рисуем список фигур                             # implement
def draw_all_shapes(shapes)                     end
  shapes.each do |shape|                      end
    case shape.class
      when Circle; draw_circle(shape)         def draw_all_shapes(shapes)
      when Square; draw_square(shape)           shapes.each do |shape|
    end                                           shape.draw
  end                                           end
end                                           end
OCP
Принцип открытости/закрытости
OCP
        Принцип открытости/закрытости
•   не существует моделей, универсальных во всех
    контекстах
OCP
        Принцип открытости/закрытости
•   не существует моделей, универсальных во всех
    контекстах

•   защищайся только от наиболее вероятных изменений
OCP
        Принцип открытости/закрытости
•   не существует моделей, универсальных во всех
    контекстах

•   защищайся только от наиболее вероятных изменений

•   не беги вперед и не перегружай код абстракциями
    (помни про ненужную сложность)
OCP
        Принцип открытости/закрытости
•   не существует моделей, универсальных во всех
    контекстах

•   защищайся только от наиболее вероятных изменений

•   не беги вперед и не перегружай код абстракциями
    (помни про ненужную сложность)

•   “Обманул меня раз - позор тебе. Обманул два - позор
    мне”
OCP
        Принцип открытости/закрытости
•   не существует моделей, универсальных во всех
    контекстах

•   защищайся только от наиболее вероятных изменений

•   не беги вперед и не перегружай код абстракциями
    (помни про ненужную сложность)

•   “Обманул меня раз - позор тебе. Обманул два - позор
    мне”

•   отказ от абстракции так же важен, как и ее ввод
Liskov substitution principle
       Принцип подстановки Лисков
LSP
Принцип подстановки Лисков
LSP
          Принцип подстановки Лисков


•   должна быть возможность вместо базового типа
    подставить любой его подтип
LSP
          Принцип подстановки Лисков


•   должна быть возможность вместо базового типа
    подставить любой его подтип

•   поведение наследуемых классов не должно
    противоречить поведению, заданному базовым классом
LSP
          Принцип подстановки Лисков


•   должна быть возможность вместо базового типа
    подставить любой его подтип

•   поведение наследуемых классов не должно
    противоречить поведению, заданному базовым классом

•   LSP - важнейший критерий оценки качества при
    построении иерархий наследования
LSP
Принцип подстановки Лисков
LSP
             Принцип подстановки Лисков
class Rectangle
  def width
    return @width
  end

  def height
    return @height
  end


  def width=(number)
    @width = number
  end

  def height=(number)
    @height = number
  end
end
LSP
             Принцип подстановки Лисков
class Rectangle                 class Square < Rectangle
  def width                       def width=(number)
    return @width                   @width = number
  end                               @height = number
                                  end
  def height
    return @height                def height=(number)
  end                               @width = number
                                    @height = number
                                  end
  def width=(number)            end
    @width = number
  end

  def height=(number)
    @height = number
  end
end
LSP
             Принцип подстановки Лисков
class Rectangle                 class Square < Rectangle
  def width                       def width=(number)
    return @width                   @width = number
  end                               @height = number
                                  end
  def height
    return @height                def height=(number)
  end                               @width = number
                                    @height = number
                                  end
  def width=(number)            end
    @width = number
  end

  def height=(number)
    @height = number
  end                            def foo(rectangle)
end                                rectangle.width = 5
                                   rectangle.height = 7
                                 end
LSP
             Принцип подстановки Лисков
class Rectangle                                          class Square < Rectangle
  def width                                                def width=(number)
    return @width                                            @width = number
  end                                                        @height = number
                                                           end
  def height
    return @height                                         def height=(number)
  end                                                        @width = number
                                                             @height = number
  # assert((width == number) && (height = old.height))     end
  def width=(number)                                     end
    @width = number
  end

  def height=(number)
    @height = number
  end                                                     def foo(rectangle)
end                                                         rectangle.width = 5
                                                            rectangle.height = 7
                                                          end
LSP
Принцип подстановки Лисков
LSP
          Принцип подстановки Лисков


•   проектирование по контракту (пред- и пост- условия)
LSP
          Принцип подстановки Лисков


•   проектирование по контракту (пред- и пост- условия)

•   производный класс не должен исключать
    функциональность базового класса
LSP
          Принцип подстановки Лисков


•   проектирование по контракту (пред- и пост- условия)

•   производный класс не должен исключать
    функциональность базового класса

•   если классы имеют некоторую общую обязанность -
    возможно имеет смысл провести факторизацию
Interface segregation principle
       Принцип разделения интерфейсов
ISP
Принцип разделения интерфейсов
ISP
       Принцип разделения интерфейсов

•   клиент не должен вынужденно зависеть от элементов
    интерфейса, которые он не использует
ISP
       Принцип разделения интерфейсов

•   клиент не должен вынужденно зависеть от элементов
    интерфейса, которые он не использует

•   “жирные” интерфейсы - плохо
ISP
       Принцип разделения интерфейсов

•   клиент не должен вынужденно зависеть от элементов
    интерфейса, которые он не использует

•   “жирные” интерфейсы - плохо

•   интерфейс считается “жирным”, когда между его
    методами слабая сцепленность (см. GRASP)
ISP
                       Принцип разделения интерфейсов

class ServiceClient
  def send_data; end
  def flush; end
end

class HttpServiceClient < ServiceClient
  def send_data
    # implement
  end

  def flush
    # пустой метод, остаток от интерфейса
  end
end

class BufferingHttpServiceClient < ServiceClient
  def send_data
    # another implement
  end

  def flush
    # implement
  end
end
ISP
                       Принцип разделения интерфейсов

class ServiceClient                                 class ServiceClient
  def send_data; end                                  def send_data; end
  def flush; end                                    end
end
                                                    class BufferingServiceClient < ServiceClient
class HttpServiceClient < ServiceClient               def flush; end
  def send_data                                     end
    # implement
  end
                                                    class HttpServiceClient < ServiceClient
  def flush                                           def send_data
    # пустой метод, остаток от интерфейса               # implement
  end                                                 end
end                                                 end

class BufferingHttpServiceClient < ServiceClient    class BufferingHttpServiceClient < BufferingServiceClient
  def send_data                                       def send_data
    # another implement                                 # another implement
  end                                                 end

  def flush                                           def flush
    # implement                                         # implement
  end                                                 end
end                                                 end
ISP
          Принцип разделения интерфейсов

Как разделять?
ISP
          Принцип разделения интерфейсов

Как разделять?
   •   делегация
ISP
          Принцип разделения интерфейсов

Как разделять?
   •   делегация

   •   множественное наследование
Dependency-inversion principle
        Принцип инверсии зависимости
DIP
Принцип инверсии зависимости
DIP
         Принцип инверсии зависимости


•   модули верхнего уровня не должны зависеть от
    модулей нижнего уровня. И те и другие должны
    зависеть от абстракций
DIP
         Принцип инверсии зависимости


•   модули верхнего уровня не должны зависеть от
    модулей нижнего уровня. И те и другие должны
    зависеть от абстракций

•   абстракции не должны зависеть от деталей. Детали
    должны зависеть от абстракций
DIP
         Принцип инверсии зависимости


•   модули верхнего уровня не должны зависеть от
    модулей нижнего уровня. И те и другие должны
    зависеть от абстракций

•   абстракции не должны зависеть от деталей. Детали
    должны зависеть от абстракций

•   если коротко: зависеть надо от абстракций
DIP
                         Принцип инверсии зависимости
      до
class Program
  def self.run
    reporter = Reporter.new
    reporter.send_reports()
  end
end

class Reporter
  def initialize
    @builder = ReportBuilder.new
    @sender = EmailReportSender.new
  end

  def send_reports
    reports = @builder.create_reports()
    reports.each do |report|
      @sender.send(report)
    end
  end
end

Program.run
DIP
                         Принцип инверсии зависимости
      до
                                           class Program
class Program                                def self.run
  def self.run
                                               # builder, sender - реализации соответствующих интерфейсов
    reporter = Reporter.new
                                               builder = ReportBuilder.new
    reporter.send_reports()
                                               sender = EmailReportSender.new
  end
                                               reporter = Reporter.new(builder, sender)
end
                                               reporter.send_reports()
                                             end
class Reporter
                                           end
  def initialize
    @builder = ReportBuilder.new
                                           class Reporter
    @sender = EmailReportSender.new
                                             def initialize(builder, sender)
  end
                                               @builder = builder
                                               @sender = sender
  def send_reports
                                             end
    reports = @builder.create_reports()
    reports.each do |report|
                                             def send_reports
      @sender.send(report)
                                               reports = @builder.create_reports()
    end
                                               reports.each do |report|
  end
                                                 @sender.send(report)
end
                                               end
                                             end
Program.run
                                           end

                                           Program.run
DIP
                         Принцип инверсии зависимости
      до                                                 после
                                           class Program
class Program                                def self.run
  def self.run
                                               # builder, sender - реализации соответствующих интерфейсов
    reporter = Reporter.new
                                               builder = ReportBuilder.new
    reporter.send_reports()
                                               sender = EmailReportSender.new
  end
                                               reporter = Reporter.new(builder, sender)
end
                                               reporter.send_reports()
                                             end
class Reporter
                                           end
  def initialize
    @builder = ReportBuilder.new
                                           class Reporter
    @sender = EmailReportSender.new
                                             def initialize(builder, sender)
  end
                                               @builder = builder
                                               @sender = sender
  def send_reports
                                             end
    reports = @builder.create_reports()
    reports.each do |report|
                                             def send_reports
      @sender.send(report)
                                               reports = @builder.create_reports()
    end
                                               reports.each do |report|
  end
                                                 @sender.send(report)
end
                                               end
                                             end
Program.run
                                           end

                                           Program.run
DIP
     Принцип инверсии зависимости

до
DIP
     Принцип инверсии зависимости

до                       после
Советую прочитать
Советую прочитать

•   GRASP - General Responsibility Assignment Software
    Patterns
Советую прочитать

•   GRASP - General Responsibility Assignment Software
    Patterns

•   Принципы, паттерны, и методики гибкой разработки на
    языке C#. // Р. С. Мартин, М. Мартин.
Советую прочитать

•   GRASP - General Responsibility Assignment Software
    Patterns

•   Принципы, паттерны, и методики гибкой разработки на
    языке C#. // Р. С. Мартин, М. Мартин.

•   Шаблоны корпоративных приложений. // М. Фаулер
Советую прочитать

•   GRASP - General Responsibility Assignment Software
    Patterns

•   Принципы, паттерны, и методики гибкой разработки на
    языке C#. // Р. С. Мартин, М. Мартин.

•   Шаблоны корпоративных приложений. // М. Фаулер

•   Предметно-ориентированное проектирование (DDD).
    Структуризация сложных систем. // Э. Эванс
Советую прочитать

•   GRASP - General Responsibility Assignment Software
    Patterns

•   Принципы, паттерны, и методики гибкой разработки на
    языке C#. // Р. С. Мартин, М. Мартин.

•   Шаблоны корпоративных приложений. // М. Фаулер

•   Предметно-ориентированное проектирование (DDD).
    Структуризация сложных систем. // Э. Эванс

•   Паттерны проектирования. // GoF
That’s all



Вопросы?

              Щербинин Александр
              undev.ru
                twitter.com/realmyst
                github.com/realmyst

More Related Content

Similar to Объектно ориентированный дизайн

SOLID Principles in the real world
SOLID Principles in the real worldSOLID Principles in the real world
SOLID Principles in the real worldEPAM
 
разработка бизнес приложений (7)
разработка бизнес приложений (7)разработка бизнес приложений (7)
разработка бизнес приложений (7)Alexander Gornik
 
Yuri Trukhin - Software developement best practices
Yuri Trukhin - Software developement best practicesYuri Trukhin - Software developement best practices
Yuri Trukhin - Software developement best practicesbeloslab
 
разработка бизнес приложений (6)
разработка бизнес приложений (6)разработка бизнес приложений (6)
разработка бизнес приложений (6)Alexander Gornik
 
Как писать красивый код или основы SOLID
Как писать красивый код или основы SOLIDКак писать красивый код или основы SOLID
Как писать красивый код или основы SOLIDPavel Tsukanov
 
Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013ScalaNsk
 
How to cook a blockchain and not get burned
How to cook a blockchain and not get burned How to cook a blockchain and not get burned
How to cook a blockchain and not get burned Alexander Syrotenko
 
Алексей Федоров
Алексей ФедоровАлексей Федоров
Алексей ФедоровCodeFest
 
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПОЕвгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПОLuxoft Education Center
 
FlatGUI: Reactive GUI Toolkit Implemented in Clojure
FlatGUI: Reactive GUI Toolkit Implemented in ClojureFlatGUI: Reactive GUI Toolkit Implemented in Clojure
FlatGUI: Reactive GUI Toolkit Implemented in Clojuredenyslebediev
 
Дизайн больших приложений в ФП
Дизайн больших приложений в ФПДизайн больших приложений в ФП
Дизайн больших приложений в ФПAlexander Granin
 
Kanban vs Scrum – чьё кунг-фу сильнее
Kanban vs Scrum – чьё кунг-фу сильнееKanban vs Scrum – чьё кунг-фу сильнее
Kanban vs Scrum – чьё кунг-фу сильнееKirill Klimov
 

Similar to Объектно ориентированный дизайн (20)

SOLID Principles in the real world
SOLID Principles in the real worldSOLID Principles in the real world
SOLID Principles in the real world
 
разработка бизнес приложений (7)
разработка бизнес приложений (7)разработка бизнес приложений (7)
разработка бизнес приложений (7)
 
Yuri Trukhin - Software developement best practices
Yuri Trukhin - Software developement best practicesYuri Trukhin - Software developement best practices
Yuri Trukhin - Software developement best practices
 
Tdd php
Tdd phpTdd php
Tdd php
 
SOLID
SOLIDSOLID
SOLID
 
L05 features
L05 featuresL05 features
L05 features
 
разработка бизнес приложений (6)
разработка бизнес приложений (6)разработка бизнес приложений (6)
разработка бизнес приложений (6)
 
Как писать красивый код или основы SOLID
Как писать красивый код или основы SOLIDКак писать красивый код или основы SOLID
Как писать красивый код или основы SOLID
 
Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013Павел Павлов - Scala для профессионалов - Joker 2013
Павел Павлов - Scala для профессионалов - Joker 2013
 
How to cook a blockchain and not get burned
How to cook a blockchain and not get burned How to cook a blockchain and not get burned
How to cook a blockchain and not get burned
 
Алексей Федоров
Алексей ФедоровАлексей Федоров
Алексей Федоров
 
Rom - Ruby Object Mapper
Rom - Ruby Object MapperRom - Ruby Object Mapper
Rom - Ruby Object Mapper
 
Ryazan
RyazanRyazan
Ryazan
 
Design Rules And Principles
Design Rules And PrinciplesDesign Rules And Principles
Design Rules And Principles
 
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПОЕвгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
Евгений Кривошеев: Фундаментальные правила и принципы проектирования ПО
 
FlatGUI: Reactive GUI Toolkit Implemented in Clojure
FlatGUI: Reactive GUI Toolkit Implemented in ClojureFlatGUI: Reactive GUI Toolkit Implemented in Clojure
FlatGUI: Reactive GUI Toolkit Implemented in Clojure
 
Refactoring
RefactoringRefactoring
Refactoring
 
Design principles
Design principles Design principles
Design principles
 
Дизайн больших приложений в ФП
Дизайн больших приложений в ФПДизайн больших приложений в ФП
Дизайн больших приложений в ФП
 
Kanban vs Scrum – чьё кунг-фу сильнее
Kanban vs Scrum – чьё кунг-фу сильнееKanban vs Scrum – чьё кунг-фу сильнее
Kanban vs Scrum – чьё кунг-фу сильнее
 

Объектно ориентированный дизайн

  • 1. Дизайн ООП правила хорошего тона. SOLID. Щербинин Александр undev.ru
  • 2. Объектно ориентированное проектирование • проектирование - суть программирования • ваш код - проектная документация продукта • задача проектирования - управление сложностью проекта
  • 3. Ароматы дизайна или как распознать загнивающий код
  • 5. Ароматы дизайна • непрозрачность
  • 6. Ароматы дизайна • непрозрачность - сложность для понимания, запутанность
  • 7. Ароматы дизайна • непрозрачность - сложность для понимания, запутанность • жесткость
  • 8. Ароматы дизайна • непрозрачность - сложность для понимания, запутанность • жесткость - сложность внесения изменений
  • 9. Ароматы дизайна • непрозрачность - сложность для понимания, запутанность • жесткость - сложность внесения изменений - простое изменение вызывает каскад зависимых правок
  • 10. Ароматы дизайна • непрозрачность - сложность для понимания, запутанность • жесткость - сложность внесения изменений - простое изменение вызывает каскад зависимых правок • хрупкость
  • 11. Ароматы дизайна • непрозрачность - сложность для понимания, запутанность • жесткость - сложность внесения изменений - простое изменение вызывает каскад зависимых правок • хрупкость - исправление одной проблемы влечет появление множества новых
  • 13. Ароматы дизайна • неподвижность
  • 14. Ароматы дизайна • неподвижность - сложно выделить части дизайна в отдельные компоненты
  • 15. Ароматы дизайна • неподвижность - сложно выделить части дизайна в отдельные компоненты • вязкость
  • 16. Ароматы дизайна • неподвижность - сложно выделить части дизайна в отдельные компоненты • вязкость - сложно сохранить дизайн при изменении
  • 17. Ароматы дизайна • неподвижность - сложно выделить части дизайна в отдельные компоненты • вязкость - сложно сохранить дизайн при изменении - решить задачу правильно сложнее, чем сделать “хак”
  • 19. Ароматы дизайна • ненужная сложность
  • 20. Ароматы дизайна • ненужная сложность - наблюдается overhead, наличие множества неиспользуемых элементов
  • 21. Ароматы дизайна • ненужная сложность - наблюдается overhead, наличие множества неиспользуемых элементов - возникает, когда разработчик пытается предугадать последующие изменения
  • 22. Ароматы дизайна • ненужная сложность - наблюдается overhead, наличие множества неиспользуемых элементов - возникает, когда разработчик пытается предугадать последующие изменения • ненужные повторения
  • 23. Ароматы дизайна • ненужная сложность - наблюдается overhead, наличие множества неиспользуемых элементов - возникает, когда разработчик пытается предугадать последующие изменения • ненужные повторения - один и тот же код с минимальными изменениями копируется снова и снова
  • 24. SOLID
  • 25. SOLID • Single responsibility principle • Open/closed principle • Liskov substitution principle • Interface segregation principle • Dependency inversion principle
  • 26. Single responsibility principle Принцип единственной обязанности
  • 28. SRP Принцип единственной обязанности • у класса должна быть только одна причина для изменения
  • 29. SRP Принцип единственной обязанности • у класса должна быть только одна причина для изменения • то есть у класса должна быть только одна обязанность
  • 30. SRP Принцип единственной обязанности • у класса должна быть только одна причина для изменения • то есть у класса должна быть только одна обязанность • сильное сцепление (high cohesion, см. GRASP)
  • 31. SRP Принцип единственной обязанности class Modem   def dial(number)   end 1-я обязанность   def hangup управление соединением   end   def send(char)   end 2-я обязанность   def recv передача данных   end end
  • 32. SRP Принцип единственной обязанности module ConnectionInterface   def dial(number)   end   def hangup class Modem   end   include ConnectionInterface end   include DataChannelInterface end module DataChannelInterface   def send(char)   end   def recv   end end
  • 33. SRP Принцип единственной обязанности class Modem   def dial(number)   end 1-я обязанность управление соединением   def hangup   end   def send(char)   end 2-я обязанность передача данных   def recv   end end Если необходимо изменить только процесс соединения - то дизайн становится жестким, и нужно разделять обязанности Если изменяется одновременно и процесс соединения, и процесс отправки - то разделение повлечет за собой излишнюю сложность
  • 34. SRP Принцип единственной обязанности class Basket   def calculate бизнес-логика   end   def store   end логика приложения end
  • 36. SRP Принцип единственной обязанности • не совмещайте бизнес-логику с логикой приложения
  • 37. SRP Принцип единственной обязанности • не совмещайте бизнес-логику с логикой приложения • если вам очень-очень нужно совместить обязанности - не создавайте зависимостей
  • 38. Open/closed principle Принцип открытости/закрытости
  • 40. OCP Принцип открытости/закрытости • программные сущности должны быть открыты для расширения, но закрыты для модификации
  • 41. OCP Принцип открытости/закрытости • программные сущности должны быть открыты для расширения, но закрыты для модификации • открыты для расширения - мы можем изменить состав модуля, добавив новое поведение
  • 42. OCP Принцип открытости/закрытости • программные сущности должны быть открыты для расширения, но закрыты для модификации • открыты для расширения - мы можем изменить состав модуля, добавив новое поведение • закрыты для модификации - расширение модуля не связано с изменением его исходного кода (уменьшаем жесткость)
  • 43. OCP Принцип открытости/закрытости Задача: необходимо рисовать геометрические фигуры (круги и квадраты) в графическом интерфейсе.
  • 44. OCP Принцип открытости/закрытости wrong (высокая жесткость) Circle = Struct.new(:radius, :center) # рисуем круг def draw_circle   # end Square = Struct.new(:side, :top_left) # рисуем квадрат def draw_square   # end # рисуем список фигур def draw_all_shapes(shapes)   shapes.each do |shape|     case shape.class       when Circle; draw_circle(shape)       when Square; draw_square(shape)     end   end end
  • 45. OCP Принцип открытости/закрытости wrong better (высокая жесткость) Circle = Struct.new(:radius, :center) class Shape   def draw # рисуем круг     # interface def draw_circle   end   # end end class Circle < Shape Square = Struct.new(:side, :top_left)   def draw     # implement # рисуем квадрат   end def draw_square end   # end class Square < Shape   def draw # рисуем список фигур     # implement def draw_all_shapes(shapes)   end   shapes.each do |shape| end     case shape.class       when Circle; draw_circle(shape) def draw_all_shapes(shapes)       when Square; draw_square(shape)   shapes.each do |shape|     end     shape.draw   end   end end end
  • 47. OCP Принцип открытости/закрытости • не существует моделей, универсальных во всех контекстах
  • 48. OCP Принцип открытости/закрытости • не существует моделей, универсальных во всех контекстах • защищайся только от наиболее вероятных изменений
  • 49. OCP Принцип открытости/закрытости • не существует моделей, универсальных во всех контекстах • защищайся только от наиболее вероятных изменений • не беги вперед и не перегружай код абстракциями (помни про ненужную сложность)
  • 50. OCP Принцип открытости/закрытости • не существует моделей, универсальных во всех контекстах • защищайся только от наиболее вероятных изменений • не беги вперед и не перегружай код абстракциями (помни про ненужную сложность) • “Обманул меня раз - позор тебе. Обманул два - позор мне”
  • 51. OCP Принцип открытости/закрытости • не существует моделей, универсальных во всех контекстах • защищайся только от наиболее вероятных изменений • не беги вперед и не перегружай код абстракциями (помни про ненужную сложность) • “Обманул меня раз - позор тебе. Обманул два - позор мне” • отказ от абстракции так же важен, как и ее ввод
  • 52. Liskov substitution principle Принцип подстановки Лисков
  • 54. LSP Принцип подстановки Лисков • должна быть возможность вместо базового типа подставить любой его подтип
  • 55. LSP Принцип подстановки Лисков • должна быть возможность вместо базового типа подставить любой его подтип • поведение наследуемых классов не должно противоречить поведению, заданному базовым классом
  • 56. LSP Принцип подстановки Лисков • должна быть возможность вместо базового типа подставить любой его подтип • поведение наследуемых классов не должно противоречить поведению, заданному базовым классом • LSP - важнейший критерий оценки качества при построении иерархий наследования
  • 58. LSP Принцип подстановки Лисков class Rectangle   def width     return @width   end   def height     return @height   end   def width=(number)     @width = number   end   def height=(number)     @height = number   end end
  • 59. LSP Принцип подстановки Лисков class Rectangle class Square < Rectangle   def width   def width=(number)     return @width     @width = number   end     @height = number   end   def height     return @height   def height=(number)   end     @width = number     @height = number   end   def width=(number) end     @width = number   end   def height=(number)     @height = number   end end
  • 60. LSP Принцип подстановки Лисков class Rectangle class Square < Rectangle   def width   def width=(number)     return @width     @width = number   end     @height = number   end   def height     return @height   def height=(number)   end     @width = number     @height = number   end   def width=(number) end     @width = number   end   def height=(number)     @height = number   end def foo(rectangle) end   rectangle.width = 5   rectangle.height = 7 end
  • 61. LSP Принцип подстановки Лисков class Rectangle class Square < Rectangle   def width   def width=(number)     return @width     @width = number   end     @height = number   end   def height     return @height   def height=(number)   end     @width = number     @height = number   # assert((width == number) && (height = old.height))   end   def width=(number) end     @width = number   end   def height=(number)     @height = number   end def foo(rectangle) end   rectangle.width = 5   rectangle.height = 7 end
  • 63. LSP Принцип подстановки Лисков • проектирование по контракту (пред- и пост- условия)
  • 64. LSP Принцип подстановки Лисков • проектирование по контракту (пред- и пост- условия) • производный класс не должен исключать функциональность базового класса
  • 65. LSP Принцип подстановки Лисков • проектирование по контракту (пред- и пост- условия) • производный класс не должен исключать функциональность базового класса • если классы имеют некоторую общую обязанность - возможно имеет смысл провести факторизацию
  • 66. Interface segregation principle Принцип разделения интерфейсов
  • 68. ISP Принцип разделения интерфейсов • клиент не должен вынужденно зависеть от элементов интерфейса, которые он не использует
  • 69. ISP Принцип разделения интерфейсов • клиент не должен вынужденно зависеть от элементов интерфейса, которые он не использует • “жирные” интерфейсы - плохо
  • 70. ISP Принцип разделения интерфейсов • клиент не должен вынужденно зависеть от элементов интерфейса, которые он не использует • “жирные” интерфейсы - плохо • интерфейс считается “жирным”, когда между его методами слабая сцепленность (см. GRASP)
  • 71. ISP Принцип разделения интерфейсов class ServiceClient   def send_data; end   def flush; end end class HttpServiceClient < ServiceClient   def send_data     # implement   end   def flush     # пустой метод, остаток от интерфейса   end end class BufferingHttpServiceClient < ServiceClient   def send_data     # another implement   end   def flush     # implement   end end
  • 72. ISP Принцип разделения интерфейсов class ServiceClient class ServiceClient   def send_data; end   def send_data; end   def flush; end end end class BufferingServiceClient < ServiceClient class HttpServiceClient < ServiceClient   def flush; end   def send_data end     # implement   end class HttpServiceClient < ServiceClient   def flush   def send_data     # пустой метод, остаток от интерфейса     # implement   end   end end end class BufferingHttpServiceClient < ServiceClient class BufferingHttpServiceClient < BufferingServiceClient   def send_data   def send_data     # another implement     # another implement   end   end   def flush   def flush     # implement     # implement   end   end end end
  • 73. ISP Принцип разделения интерфейсов Как разделять?
  • 74. ISP Принцип разделения интерфейсов Как разделять? • делегация
  • 75. ISP Принцип разделения интерфейсов Как разделять? • делегация • множественное наследование
  • 76. Dependency-inversion principle Принцип инверсии зависимости
  • 78. DIP Принцип инверсии зависимости • модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций
  • 79. DIP Принцип инверсии зависимости • модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций • абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
  • 80. DIP Принцип инверсии зависимости • модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций • абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций • если коротко: зависеть надо от абстракций
  • 81. DIP Принцип инверсии зависимости до class Program   def self.run     reporter = Reporter.new     reporter.send_reports()   end end class Reporter   def initialize     @builder = ReportBuilder.new     @sender = EmailReportSender.new   end   def send_reports     reports = @builder.create_reports()     reports.each do |report|       @sender.send(report)     end   end end Program.run
  • 82. DIP Принцип инверсии зависимости до class Program class Program   def self.run   def self.run # builder, sender - реализации соответствующих интерфейсов     reporter = Reporter.new     builder = ReportBuilder.new     reporter.send_reports()     sender = EmailReportSender.new   end     reporter = Reporter.new(builder, sender) end     reporter.send_reports()   end class Reporter end   def initialize     @builder = ReportBuilder.new class Reporter     @sender = EmailReportSender.new   def initialize(builder, sender)   end     @builder = builder     @sender = sender   def send_reports   end     reports = @builder.create_reports()     reports.each do |report|   def send_reports       @sender.send(report)     reports = @builder.create_reports()     end     reports.each do |report|   end       @sender.send(report) end     end   end Program.run end Program.run
  • 83. DIP Принцип инверсии зависимости до после class Program class Program   def self.run   def self.run # builder, sender - реализации соответствующих интерфейсов     reporter = Reporter.new     builder = ReportBuilder.new     reporter.send_reports()     sender = EmailReportSender.new   end     reporter = Reporter.new(builder, sender) end     reporter.send_reports()   end class Reporter end   def initialize     @builder = ReportBuilder.new class Reporter     @sender = EmailReportSender.new   def initialize(builder, sender)   end     @builder = builder     @sender = sender   def send_reports   end     reports = @builder.create_reports()     reports.each do |report|   def send_reports       @sender.send(report)     reports = @builder.create_reports()     end     reports.each do |report|   end       @sender.send(report) end     end   end Program.run end Program.run
  • 84. DIP Принцип инверсии зависимости до
  • 85. DIP Принцип инверсии зависимости до после
  • 87. Советую прочитать • GRASP - General Responsibility Assignment Software Patterns
  • 88. Советую прочитать • GRASP - General Responsibility Assignment Software Patterns • Принципы, паттерны, и методики гибкой разработки на языке C#. // Р. С. Мартин, М. Мартин.
  • 89. Советую прочитать • GRASP - General Responsibility Assignment Software Patterns • Принципы, паттерны, и методики гибкой разработки на языке C#. // Р. С. Мартин, М. Мартин. • Шаблоны корпоративных приложений. // М. Фаулер
  • 90. Советую прочитать • GRASP - General Responsibility Assignment Software Patterns • Принципы, паттерны, и методики гибкой разработки на языке C#. // Р. С. Мартин, М. Мартин. • Шаблоны корпоративных приложений. // М. Фаулер • Предметно-ориентированное проектирование (DDD). Структуризация сложных систем. // Э. Эванс
  • 91. Советую прочитать • GRASP - General Responsibility Assignment Software Patterns • Принципы, паттерны, и методики гибкой разработки на языке C#. // Р. С. Мартин, М. Мартин. • Шаблоны корпоративных приложений. // М. Фаулер • Предметно-ориентированное проектирование (DDD). Структуризация сложных систем. // Э. Эванс • Паттерны проектирования. // GoF
  • 92. That’s all Вопросы? Щербинин Александр undev.ru twitter.com/realmyst github.com/realmyst

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n