Wyobraź sobie, że Twoi klienci chcą mieć możliwość dodania nowej właściwości (np. Koloru) do produktu w swoim sklepie internetowym w CMS.
Zamiast mieć właściwości jako pola:
class Car extends Product {
protected String type;
protected int seats;
}
Prawdopodobnie skończyłbyś robić coś takiego:
class Product {
protected String productName;
protected Map<String, Property> properties;
}
class Property {
protected String name;
protected String value;
}
To znaczy tworzenie własnego systemu typów na istniejącym. Wydaje mi się, że może to być postrzegane jako tworzenie języka specyficznego dla domeny, czy nie?
Czy to podejście jest znanym wzorcem projektowym? Czy rozwiązałbyś problem inaczej? Wiem, że istnieją języki, w których mogę dodać pole w czasie wykonywania, ale co z bazą danych? Wolisz dodawać / zmieniać kolumny lub używać czegoś, jak pokazano powyżej?
Dziękuję za Twój czas :).
Odpowiedzi:
Gratulacje! Właśnie okrążyłeś globus systemu / języka programowania, przybywając na drugą stronę świata, skąd odszedłeś. Właśnie wylądowałeś na granicy dynamicznego obiektu opartego na języku / prototypie!
Wiele języków dynamicznych (np. JavaScript, PHP, Python) umożliwia rozszerzenie lub zmianę właściwości obiektu w czasie wykonywania.
Skrajną formą tego jest język oparty na prototypach, taki jak Self lub JavaScript. Nie mają zajęć, ściśle mówiąc. Możesz robić rzeczy, które wyglądają jak oparte na klasach, obiektowe programowanie z dziedziczeniem, ale reguły są znacznie bardziej rozluźnione w porównaniu do ostrzej zdefiniowanych języków opartych na klasach, takich jak Java i C #.
Langauges, takie jak PHP i Python, żyją pośrodku. Mają regularne, idiomatyczne systemy oparte na klasach. Ale atrybuty obiektów mogą być dodawane, zmieniane lub usuwane w czasie wykonywania - aczkolwiek z pewnymi ograniczeniami (takimi jak „oprócz typów wbudowanych”), których nie można znaleźć w JavaScript.
Dużym kompromisem dla tego dynamizmu jest wydajność. Zapomnij, jak mocno lub słabo wpisany jest język lub jak dobrze można go skompilować do kodu maszynowego. Obiekty dynamiczne muszą być reprezentowane jako elastyczne mapy / słowniki, a nie proste struktury. Zwiększa to dostęp do każdego obiektu. Niektóre programy dokładają wszelkich starań, aby zmniejszyć ten narzut (np. Z przypisywaniem kwantowego kwarga i klasami opartymi na slotach w Pythonie), ale dodatkowy narzut jest zwykle równy kursowi i cenie wstępu.
Wracając do swojego projektu, przeszczepiasz zdolność posiadania dynamicznych właściwości na podzbiorze swoich klas. A
Product
może mieć zmienne atrybuty; przypuszczalnie takInvoice
czyOrder
nie i nie mógł. To nie jest zła droga. Daje ci elastyczność, aby mieć zmienne tam, gdzie jest to potrzebne, pozostając w ścisłym, zdyscyplinowanym języku i systemie pisma. Z drugiej strony jesteś odpowiedzialny za zarządzanie tymi elastycznymi właściwościami i prawdopodobnie będziesz musiał to zrobić za pomocą mechanizmów, które wyglądają nieco inaczej niż bardziej natywne atrybuty.p.prop('tensile_strength')
zamiastp.tensile_strength
na przykład ip.set_prop('tensile_strength', 104.4)
raczej niżp.tensile_strength = 104.4
. Ale pracowałem z wieloma programami w Pascal, Ada, C, Java, a nawet w językach dynamicznych, i korzystałem z nich, a także korzystałem z takiego dostępu do getter-setter dla niestandardowych typów atrybutów; podejście jest wyraźnie wykonalne.Nawiasem mówiąc, napięcie między typami statycznymi a bardzo zróżnicowanym światem jest niezwykle powszechne. Analogiczny problem często pojawia się podczas projektowania schematu bazy danych, szczególnie w relacyjnych i przed relacyjnych magazynach danych. Czasami rozwiązuje się to poprzez tworzenie „superrzędów”, które zawierają wystarczającą elastyczność, aby pomieścić lub zdefiniować połączenie wszystkich wymyślonych odmian, a następnie upchnąć dane, które pojawią się w tych polach. WordPress
wp_posts
stół , na przykład, ma pola, na przykładcomment_count
,ping_status
,post_parent
ipost_date_gmt
że są tylko interesujące w pewnych okolicznościach, i że w praktyce często wygaszony. Innym podejściem jest bardzo oszczędny, znormalizowany stółwp_options
podobny do twojegoProperty
klasa. Chociaż wymaga to bardziej precyzyjnego zarządzania, elementy w nim rzadko są puste. Zorientowane obiektowo i dokumentacyjne bazy danych (np. MongoDB) często łatwiej radzą sobie ze zmianą opcji, ponieważ mogą tworzyć i ustawiać atrybuty praktycznie do woli.źródło
Podoba mi się pytanie, moje dwa centy:
Twoje dwa podejścia są radykalnie różne:
W C ++ wielu użyłoby std :: map of boost :: variant, aby osiągnąć połączenie obu.
Disgression: Należy pamiętać, że niektóre języki, takie jak C #, umożliwiają dynamiczne tworzenie typów. Co może być dobrym rozwiązaniem ogólnego problemu dynamicznego dodawania członków. Jednak „modyfikowanie / dodawanie” typów po kompilacji powoduje uszkodzenie samego systemu typów i sprawia, że „zmodyfikowane” typy są prawie bezużyteczne (np. Jak uzyskasz dostęp do takich dodanych właściwości, skoro nawet nie wiesz, że istnieją? Jedyny rozsądny sposób bądź systematyczną refleksją nad każdym obiektem ... kończąc na czystym dynamicznym języku _ możesz odwołać się do słowa kluczowego „dynamicznego” .NET)
źródło
Tworzenie typu w środowisku wykonawczym wydaje się znacznie bardziej skomplikowane niż tworzenie warstwy abstrakcji. Bardzo często tworzy się abstrakcję w celu oddzielenia systemów.
Pokażę przykład z mojej praktyki. Giełda w Moskwie ma rdzeń handlowy o nazwie Plaza2 z API handlowca. Handlowcy piszą swoje programy do pracy z danymi finansowymi. Problem polega na tym, że dane te są bardzo ogromne, złożone i bardzo narażone na zmiany. Może ulec zmianie po wprowadzeniu nowego produktu finansowego lub zmianie ról rozliczeniowych. Charakter przyszłych zmian nie można przewidzieć. Dosłownie może się zmieniać każdego dnia, a słabi programiści powinni edytować kod i wydawać nową wersję, a źli inwestorzy powinni zmieniać swoje systemy.
Oczywistą decyzją jest ukrycie całego finansowego piekła za abstrakcją. Wykorzystali dobrze znaną abstrakcję tabel SQL. Największa część ich rdzenia może współpracować z dowolnym prawidłowym schematem, tak samo jak oprogramowanie tradera może dynamicznie analizować schemat i sprawdzać, czy jest on zgodny z ich systemem.
Wracając do twojego przykładu, normalne jest tworzenie abstrakcyjnego języka przed jakąś logiką. Może to być „własność”, „tablica”, „wiadomość”, a nawet ludzki język, ale zwróć uwagę na wady tego podejścia:
źródło
W XML i HTML byłyby to atrybuty węzła / elementu. Słyszałem też, że nazywają się właściwościami rozszerzonymi, parami nazwa / wartość i parametrami.
Tak rozwiązałbym problem, tak.
W pewnym sensie baza danych byłaby jak Java. W pesudo-sql:
Odpowiadałoby to Javie
Zauważ, że nie ma potrzeby posiadania klasy Właściwość, ponieważ Mapa przechowuje nazwę jako klucz.
Próbowałem dodać / zmienić kolumnę i to był koszmar. Można to zrobić, ale rzeczy wciąż się nie synchronizują, a ja nigdy nie działałem dobrze. Struktura tabeli, którą opisałem powyżej, odniosła większy sukces. Jeśli trzeba zrobić wyszukiwań i porządku przez tych, rozważyć użycie atrybutów tabeli dla każdego typu danych (
date_attributes
,currency_attributes
etc.) lub dodając niektóre właściwości jak starych dobrych kolumn w tabeli bazy danych produktów. Raporty są często znacznie łatwiejsze do pisania w kolumnach bazy danych niż w podtabelach.źródło