Łańcuchowe wywoływanie metod
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 81 z 107.
Łączenie metod to technika, w której metody zwracają odbiorcę, umożliwiając łączenie wielu wywołań metod w jednym poleceniu. Wzorzec Builder już wykorzystuje tę technikę, ale jest to samodzielna koncepcja przydatna w wielu kontekstach.
Kluczem jest zwracanie wskaźnika do struktury z każdej metody:
type Query struct {
table string
columns []string
where string
limit int
}
func (q *Query) Select(cols ...string) *Query {
q.columns = cols
return q
}
func (q *Query) From(table string) *Query {
q.table = table
return q
}
func (q *Query) Where(condition string) *Query {
q.where = condition
return q
}
func (q *Query) Limit(n int) *Query {
q.limit = n
return q
}Teraz możesz płynnie łączyć te wywołania:
query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)W porównaniu z osobnymi instrukcjami ten zapis przypomina niemal język naturalny. Łańcuchowe wywoływanie metod sprawdza się najlepiej, gdy operacje konfigurują lub modyfikują obiekt i nie muszą zwracać innych wartości. Jest powszechnie stosowane w kreatorach zapytań, interfejsach API do konfiguracji i bibliotekach asercji testowych.
Pamiętaj, że każda metoda musi używać odbiornika wskaźnikowego i zwracać *StructType, aby łańcuch działał prawidłowo.
Wyzwanie
ŁatwyZbudujmy elastyczny kreator elementów HTML, który wykorzystuje łączenie metod do tworzenia tagów HTML z atrybutami i treścią! To praktyczne zastosowanie łączenia metod: utworzysz API, które podczas budowania elementów HTML będzie niemal przypominać język naturalny.
Podzielisz kod na dwa pliki:
element.go: Zdefiniuj kreator elementów HTML za pomocą metod, które można łączyć.Utwórz strukturę
Element, która przechowuje nazwę tagu, mapę atrybutów (ciąg znaków na ciąg znaków) oraz wewnętrzną treść. Zbuduj metody, które można łączyć i z których każda zwraca*Element:NewElement(tag string)- konstruktor tworzący nowy element z podaną nazwą tagu i zainicjalizowaną (pustą) mapą atrybutówAttr(key, value string)- dodaje atrybut do elementuID(id string)- metoda pomocnicza ustawiająca atrybut "id"Class(class string)- metoda pomocnicza ustawiająca atrybut "class"Content(text string)- ustawia wewnętrzną treść elementu
Dodaj metodę
Render(), która zwraca ciąg znaków HTML. Atrybuty powinny być ułożone alfabetycznie według klucza. Format:<tag attr1="value1" attr2="value2">content</tag>Jeśli nie ma atrybutów, po prostu wyrenderuj
<tag>content</tag>. Jeśli nie ma treści, wyrenderuj<tag attr="value"></tag>.main.go: Buduj elementy HTML, łącząc metody.Wczytaj nazwę tagu, a następnie liczbę operacji do wykonania. Każda operacja to jeden z elementów:
id, po którym następuje wartość IDclass, po którym następuje wartość klasyattr, po którym następuje nazwa i wartość atrybutu (dwa wiersze)content, po którym następuje tekst treści
Połącz wszystkie operacje w jeden płynny łańcuch metod, a następnie wyświetl wyrenderowany kod HTML.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: Nazwa tagu
- Wiersz 2: Liczba operacji
- Kolejne wiersze: Typ operacji i wartość lub wartości
Na przykład dla danych:
div
3
id
container
class
main-content
content
Hello WorldWynik powinien wyglądać tak:
<div class="main-content" id="container">Hello World</div>A dla danych:
a
3
attr
href
https://example.com
attr
target
_blank
content
Click hereWynik powinien wyglądać tak:
<a href="https://example.com" target="_blank">Click here</a>A dla danych:
input
2
attr
type
text
attr
placeholder
Enter nameWynik powinien wyglądać tak:
<input placeholder="Enter name" type="text"></input>A dla danych:
p
1
content
Simple paragraphWynik powinien wyglądać tak:
<p>Simple paragraph</p>Zwróć uwagę, jak łączenie metod pozwala budować elementy krok po kroku w czytelny sposób. Każda metoda modyfikuje element i zwraca go, co pozwala bezpośrednio łączyć kolejne wywołania metod.
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Wczytaj nazwę tagu
scanner.Scan()
tagName := scanner.Text()
// Wczytaj liczbę operacji
scanner.Scan()
numOps, _ := strconv.Atoi(scanner.Text())
// Utwórz element
element := NewElement(tagName)
// Przetwórz każdą operację
for i := 0; i < numOps; i++ {
scanner.Scan()
operation := scanner.Text()
// TODO: W zależności od typu operacji wywołaj odpowiednią metodę w łańcuchu
// Możliwe operacje: "id", "class", "attr" lub "content"
// - Dla "id": wczytaj następny wiersz jako wartość ID
// - Dla "class": wczytaj następny wiersz jako wartość klasy
// - Dla "attr": wczytaj następne DWA wiersze (nazwę atrybutu, a potem jego wartość)
// - Dla "content": wczytaj następny wiersz jako treść
// Użyj łańcuchowego wywoływania metod, aby płynnie zbudować element
_ = operation // Usuń ten wiersz podczas implementacji
}
// Wypisz wyrenderowany HTML
fmt.Println(element.Render())
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online