Разкриване на интерфейс
Едно от твърденията ми бе, че доброто ООП внимателно подбира интерфейса. Нека разгледаме една ситуация.
Представете си, че имаме нужда да конструираме колекцията на части. За целта решаваме да я направим mutable
. Collection#parse
ще конструира празни колекции и ще добавя песните една по една.
Имаме два начина да реализираме това. Първият е като предлагаме масива с песни навън:
class Collection
attr_reader :songs
def initialize
@songs = []
end
end
extract_songs(songs_as_text).each do |song|
collection.songs << song
end
Вторият е да направим специален метод за целта:
class Collection
def initialize
@songs = []
end
def add_song(song)
@songs << song
end
end
extract_songs(songs_as_text).each do |song|
collection.add_song song
end
Второто е по-добрия интерфейс. Има няколко причини.
- Скрива вътрешното представяне. Така не е ясно по какъв начин се пазят песните. Може да е масив, може да е структура за по-бързо търсене. Ако трябва да минем от едното към другото, това става без промяна на клиентския код.
- Предлага по-ограничен интерфейс. Това е хубаво. В първия вариант можем да добавяме песни, да ги трием и да ги сортираме. Това не е нещо нужно - вместо това е инцидентно следствие на реализацията. Ако клиентския код започне да зависи от тези възможности и в последствие решим да променим имплементацията, има възможност да вкараме бъгове.
- Има по-малко действия, които може да правим с една колекция. Това е хубаво, понеже така колекцията се разбира по-лесно. Отново, много (брой) малки (големина) конкретни абстракции е по-добре в обектно-ориентирана среда.
Бързодействие и четимост
Любопитно е да споменем кога първия вариант е за предпочитане. Това са ситуации, в които гоним производимост. Ако имаме нужда да добавяме много песни и overhead-а от извикване на метод/функция е осезаем, излагането на вътрешно представяне може да ускори нещата.
Два неща, обаче.
Първо, това е нещо до което кода се развива. Демек, първо го правите работещ, после го правите разбираем и най-накрая, ако и където има нужда го правите бърз. Второ, Martin Fowler казва, че "оптимизацията е обратния процес на рефакторирането". Правим кода бърз, но по-трудно четим. Ще забележите, че ние често ще правим кода по-бавен (маргинално), но по-четим.
Второ, ако имате такава ситуация в Ruby, тя ще бъде рядка. Едва ли ще ви се случи в рамките на този курс.
Тук има по-обща мисъл за ценности. Кое е по-важно - четимост или производителност?
Отговора е: зависи. Трябва контекст. Има проекти, за които производителността е по-важна (search engine, web server, kernel). Има проекто, за които четимостта е по-важна (приложения - например онлайн магазин или дори този сайт). Избора на езика зависи от тези ценности.