Паттерн "State"

З подачі свого друга та начальника, сьогодні прочитав про такий паттерн програмування, як State. Лише на англомовній Вікіпедії знайшлась зрозуміла хоч трохи стаття. Цим постом намагатимусь лаконічно та доступно передати суть паттерну та навести приклад.

Отож, що це за паттерн та навіщо він потрібен. Він використовується у ситуаціях, коли у нас є якийсь об’єкт класу (Context), який може поводитись по-різному в різних ситуаціях. Ситуацію визначає якась змінна. Точніше, її значення (State). Поведінку нашого об’єкту визначає клас змінної-прапорцю стану (Concrete State). Така реалізація доцільна, коли потрібно, скажімо, прибрати глобальні змінні — стан об’єкту зберігається в самому об’єкті, а поведінку об’єкту визначає тип (класс) стану.

На прикладі повинно звучати простіше: нехай у нас є Програміст. Програміст може працювати по-різному — він може бути Веб-розробником , може бути Дизайнером , а може писати іграшки ( Гейм-девелопер або Іграшкокльоп ). Скажімо, Програміст в нас розумний. Те, «ким» працює Програміст визначає його поточний Проект. Припустимо, фірма, на якій працює Програміст широкого профілю та дозволяє працювати своїм робітникам в різних напрямках.

Тож, у нас є класс Програміст. Він має властивість — поточна_спеціальність. Властивість поточна_спеціальність — це об’єкт класу Дизайнер , Вебщик або Іграшкокльоп. Всі ці три класи повинні мати щось спільне, аби змінна могла приймати значення будь-якого з них. Тому логічно наслідувати ці три класи від абстрактного класу Спеціальність. Так як іграшки пишуться переважно на Сі або Сі++, веб-сайти на PHP або Ruby (Ruby On Rails), а дизайн переважно робиться з HTML5 + CSS3, то Програміст в конкретний момент часу може писати код на одній з цих трьох мов програмування / з використанням цих технологій. Після завершення роботи над поточним проектом, Програміст повинен перейти до роботи над новим. Тому абстрактний клас Спеціальність буде мати один-єдиний метод, писати_код(мова_програмування). А клас Програміст матиме три методи: почати_проект(тип_проекту), закінчити_проект(тип_наступного_проекту) та писати_код().

Як працює цей паттерн: у Програміста є якийсь проект. Тому його поточна Спеціальність залежить від проекту. Проект закінчується, Програмістові дають новий проект і, відповідно, його Спеціальність змінюється. Це змінює і код, який він пише, і середовище розробки, що він використовує і стиль коду, якого він дотримується. Незмінним залишається лише одне: _ Програміст пише код_. Ну, хіба, коли він на роботі. Тому що в обідню перерву він не працює. І коли не на роботі він теж не працює. Над проектами. По Спеціальності. Програміст в нас дуже принциповий. Але не будемо вдаватись в такі вже подробиці.

Реалізуємо паттерн на мові Ruby :

class Programmer
    def initialize
        @speciality = FreeLancer.new "VB.NET"
    end

    def start_project(project_type)
        if project_type == 'web' then
            @speciality = WebDeveloper.new "Ruby (with Ruby On Rails)"
        elsif project_type == 'game' then
            @speciality = GameDeveloper.new "C++ (with Horde3D)"
        elsif project_type == 'design' then
            @speciality = WebDesigner.new "HTML5 (with CSS3)"
        else
            @speciality = FreeLancer.new "PHP"
        end
    end

    def end_project(new_project_type)
        puts "My project has ended! Yay!"

        start_project(new_project_type)
    end

    def write_code()
        @speciality.write_code
    end
end

class Speciality
    def initialize(project_language)
        @programming_language = project_language
    end

    def write_code()
        # Yeah, this method is abstract for sure!
        raise "MethodHasNoImplementation"
    end
end

class GameDeveloper < Speciality
    def write_code()
        puts "I am creating a new Atari game using #{ @programming_language }"
    end
end

class WebDeveloper < Speciality
    def write_code()
        puts "I am creating a new Google Web-service using #{ @programming_language }"
    end
end

class WebDesigner < Speciality
    def write_code()
        puts "I am doing a new Twitter Bootstrap design with #{ @programming_language }"
    end
end

class FreeLancer < Speciality
    def write_code()
        puts "I am a free-lancer. My current project uses #{ @programming_language }"
    end
end

# MAIN
project_types = ['web', 'game', 'design', 'none']

programmer = Programmer.new

10.times do |level|
    current_project_type = project_types[rand(project_types.size)]
    future_project_type = project_types[rand(project_types.size)]

    puts "My level is #{ level }"
    programmer.start_project current_project_type
    programmer.write_code
    programmer.end_project future_project_type
end

Запустивши Програміста на десять довільних проектів, побачимо в терміналі приблизно таке:

My level is 0

I am creating a new Google Web-service using Ruby (with Ruby On Rails)

My project has ended! Yay!

My level is 1

I am doing a new Twitter Bootstrap design with HTML5 (with CSS3)

My project has ended! Yay!

My level is 2

I am creating a new Google Web-service using Ruby (with Ruby On Rails)

My project has ended! Yay!

My level is 3

I am doing a new Twitter Bootstrap design with HTML5 (with CSS3)

My project has ended! Yay!

My level is 4

I am a free-lancer. My current project uses PHP

My project has ended! Yay!

My level is 5

I am creating a new Atari game using C++ (with Horde3D)

My project has ended! Yay!

My level is 6

I am creating a new Atari game using C++ (with Horde3D)

My project has ended! Yay!

My level is 7

I am creating a new Atari game using C++ (with Horde3D)

My project has ended! Yay!

My level is 8

I am a free-lancer. My current project uses PHP

My project has ended! Yay!

My level is 9

I am a free-lancer. My current project uses PHP

My project has ended! Yay!

Сподіваюсь ця стаття стане бодай комусь в нагоді!..

© 2009 - 2020, Розробка - соціальна ІТ спільнота.
Контакти: info@rozrobka.com
Правила користування