הקדמה
כתובת קוד גמיש, משתמשים ומודולארים הוא חיוני לפיתוח תוכנות גמישות. עבודה בדרך זו מובטחת שהקוד יהיה קל יותר לשמר דרך הדילגמניה מהצורך בשינוי אותו במקומות רבים. איך שתעשה את זה משנה מדובר בשפה במידה מסויימת. לדוגמה, היררכיה היא דרך רגילה שמשמשת בשפות כמו ג' אווה, C++, C# ועוד.
מפתחים יכולים גם להשיג את אותם מטרות העיצוב באמצעות התצרף. התצרף הוא דרך לשילב עצמים או מסוגי מידע לאחדים מורכבים יותר. זו הדרך שגו משתמש בה לעדד שימוש בקוד, מודולריות וגמישות. הממשקים בגו מספקים שיטה לארגון התצרפויות מורכבות, ולמדינות איך להשתמש בהם יתאפשר לך ליצור קוד משובץ נפוץ ומוחזר יכול.
במאמר זה, נלמד איך לתת צורות מותאמות אישית שיש התנהגויות משותפות, שיאפשר לנו לשימוש מחדש בקוד שלנו. נלמד גם איך ליישם ממשקים עבור הצורות האישיות שלנו שיספקו את הממשקים המוגדרים מתוך ערימה אחרת.
הגדרה של התנהגות
אחד מהיישומים הבסיסיים של הקומפוזציה הוא שימוש בממשקים. ממשק מגדיר התנהגות של סוג. אחד מהממשקים הכי נפוצים בספרית הסטנדרט של Go הוא הממשק fmt.Stringer
:
השורה הראשונה בקוד מגדירה סוג שנקרא Stringer
. אחר כך היא מצטטה שהיא ממשק משולש. כמו בהגדרת מבנה, Go משתמש בפסים פלורלים ({}
) כדי להקיף את ההגדרה של הממשק. בהשוואה להגדרת מבנים, אנחנו מגדירים רק את ההתנהגות של הממשק; כלומר, "מה שהסוג הזה יכול לעשות".
במקרה של הממשק Stringer
, ההתנהגות היחידה היא השימוש בשיבוט String()
. השימוש לא לוקח את שום אג'נדה ומחזירה שטרים.
באמת באחר אנחנו נסתכל על קוד שמשתמש בהתנהגות fmt.Stringer
:
הדבר הראשון שאנחנו עושים הוא ליצור סוג חדש שנקרא Article
. לסוג זה יש שדה Title
ושדה Author
ושניהם מסוג שטרים:מינורמל:
בהמשך, אנחנו מגדירים method
שנקרא String
על סוג Article
. שימוש בשיבוט String
יחזיר שטר שמייצג סוג Article
:
אז, בmain
function שלנו, אנחנו יוצרים מקום מסוג Article
ואושרים אותו למשתנה שנקרא a
. אנחנו מעניקים את הערכים "Understanding Interfaces in Go"
לשדה Title
ו"Sammy Shark"
לשדה Author
:
אז, אנחנו מדפיסים את תוצאת השיטה String
על-ידי קריאה לfmt.Println
ועל-ידי העלאה של תוצאת קריאת a.String()
:
אחרי שהיא מופעלת התוכנית, תראו את הפלט הבא:
OutputThe "Understanding Interfaces in Go" article was written by Sammy Shark.
עד כה, לא השתמשנו באינטרף, אבל יצרנו סוג שהציג התנהגות. התנהגות זו תואמה לאינטרף fmt.Stringer
. בהמשך, בואו נראה איך אנחנו יכולים להשתמש בתנהגות זו כדי להפוך את הקוד שלנו ליותר משתמשים.
Defining an Interface
עכשיו שיצרנו את הסוג עם התנהגות הרצועה, אנחנו יכולים להסתכל על איך להשתמש בתנהגות זו.
לפני שנעשה את זה, בואו נסתכל מה אנחנו יכולים לעשות אם נרצה לקרוא את השיטה String
מסוג Article
בפונקציה:
package main
import "fmt"
type Article struct {
Title string
Author string
}
func (a Article) String() string {
return fmt.Sprintf("The %q article was written by %s.", a.Title, a.Author)
}
func main() {
a := Article{
Title: "Understanding Interfaces in Go",
Author: "Sammy Shark",
}
Print(a)
}
func Print(a Article) {
fmt.Println(a.String())
}
בקוד הזה אנחנו מוסיפים פונקציית חדשה בשם Print
שלוקחת עבורה Article
כתג. שימו לב שהיחידה של Print
עושה זה לקרוא את השיטה String
. בגלל זה, אנחנו יכולים להגדיר במקום את הממשק הזה:
package main
import "fmt"
type Article struct {
Title string
Author string
}
func (a Article) String() string {
return fmt.Sprintf("The %q article was written by %s.", a.Title, a.Author)
}
type Stringer interface {
String() string
}
func main() {
a := Article{
Title: "Understanding Interfaces in Go",
Author: "Sammy Shark",
}
Print(a)
}
func Print(s Stringer) {
fmt.Println(s.String())
}
כאן אנחנו יוצרים ממשק בשם Stringer
:
הממשק Stringer
עובדת רק עם שיחת אחת, בשם String()
שחוזרת על מקבלת סיסמאות string
. שיחה היא פונקצייה מיוחדת שמוגבלת בסוג מסויים בגו. שונה מהפונקצייה, השיחה יכולה להיות קרואה רק מהמקבל של הסוג שבו היא נוצרה.
אחר כך אנחנו מעדכנים את החתימה של השיחה Print
בדרך שיקבלה Stringer
, ולא סוג המסגרת Article
המסויימת. בגלל שהמארץ יודע שממשק Stringer
מגדיר את השיחה String
, הוא יקבל סוגים שגם יש להם את השיחה String
.
עכשיו אנחנו יכולים להשתמש בשיחה Print
עם כל דבר שמומלץ על הממשק Stringer
. בואו ניצור סוג נוסף כדי להדגים את זה:
package main
import "fmt"
type Article struct {
Title string
Author string
}
func (a Article) String() string {
return fmt.Sprintf("The %q article was written by %s.", a.Title, a.Author)
}
type Book struct {
Title string
Author string
Pages int
}
func (b Book) String() string {
return fmt.Sprintf("The %q book was written by %s.", b.Title, b.Author)
}
type Stringer interface {
String() string
}
func main() {
a := Article{
Title: "Understanding Interfaces in Go",
Author: "Sammy Shark",
}
Print(a)
b := Book{
Title: "All About Go",
Author: "Jenny Dolphin",
Pages: 25,
}
Print(b)
}
func Print(s Stringer) {
fmt.Println(s.String())
}
אנחנו עכשיו מוסיפים סוג שני בשם Book
. יש לו גם שיחה String
מוגדרה. זה אומר שהוא גם ממלץ על הממשק Stringer
. בגלל זה, אנחנו יכולים גם לשלוח אותו לפונקציית הPrint
שלנו:
OutputThe "Understanding Interfaces in Go" article was written by Sammy Shark.
The "All About Go" book was written by Jenny Dolphin. It has 25 pages.
עד כה, הראינו איך להשתמש במשתמש אחד בלבד. אף על פי שלמשתמש יכול להיות יותר מאחד התנהגות מוגדרה. בהמשך, נראה איך אנחנו יכולים להפוך את המשתמשים שלנו ליותר גיווניים על-ידי הצעת יותר פעמים של שימוש בשיטות.
התנהגויות רבות במשתמש
אחד העקרונות המרכזיים בכתיבת קוד גו הוא לכתוב סוגים קטנים וקצרים ולהרכיב אותם בכדי ליצור סוגים גדולים יותר ומורכבים. אותו הדבר נכון גם כשאנחנו מרכיבים משתמשים. כדי לראות איך אנחנו בונים משתמש, נתחיל על-ידי הגדרת משתמש אחד בלבד. נהגדר שני צורות, גלולה וריבוע, ושניהם יוגדרו שירות בשם Area
. שירות זה יחזיר את האזור הגאומטרי של הצורה שלהם:
בגלל שכל סוג מדeclares the Area
method, we can create an interface that defines that behavior. We create the following Sizer
interface:
We then define a function called Less
that takes two Sizer
and returns the smallest one:
Notice that we not only accept both arguments as the type Sizer
, but we also return the result as a Sizer
as well. This means that we no longer return a Square
or a Circle
, but the interface of Sizer
.
Finally, we print out what had the smallest area:
Output{Width:5 Height:10} is the smallest
עכשיו, בואו נהיה מספקים למעשה תנהגות נוספת לכל סוג. הפעם, נוסף את השיטה String()
שמחזירה שפת מסויימת. זה יספק את הפתרון fmt.Stringer
:
package main
import (
"fmt"
"math"
)
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * math.Pow(c.Radius, 2)
}
func (c Circle) String() string {
return fmt.Sprintf("Circle {Radius: %.2f}", c.Radius)
}
type Square struct {
Width float64
Height float64
}
func (s Square) Area() float64 {
return s.Width * s.Height
}
func (s Square) String() string {
return fmt.Sprintf("Square {Width: %.2f, Height: %.2f}", s.Width, s.Height)
}
type Sizer interface {
Area() float64
}
type Shaper interface {
Sizer
fmt.Stringer
}
func main() {
c := Circle{Radius: 10}
PrintArea(c)
s := Square{Height: 10, Width: 5}
PrintArea(s)
l := Less(c, s)
fmt.Printf("%v is the smallest\n", l)
}
func Less(s1, s2 Sizer) Sizer {
if s1.Area() < s2.Area() {
return s1
}
return s2
}
func PrintArea(s Shaper) {
fmt.Printf("area of %s is %.2f\n", s.String(), s.Area())
}
בגלל ששני הסוגים Circle
ו Square
מביאים שני השיטות Area
ו String
, אנחנו יכולים עכשיו ליצור אינטרפאציה נוספת שתואמת עם קבוצה רחבה יותר של התנהגויות. בכדי לעשות את זה, ניצור אינטרף שנקרא Shaper
. אנחנו נרכש את זה מהאינטרף Sizer
והאינטרף fmt.Stringer
:
הערה: נחשב זאת כאידיאוטית לנסות לקרוא לאינטרף שלך בסוף ב er
, כמו fmt.Stringer
, io.Writer
, וכו '. זו הסיבה שנקרא לאינטרף שלנו Shaper
ולא Shape
.
עכשיו נוכל ליצור פונקציה שנקראת PrintArea
שמקבלת אינטרף Shaper
כאן. זה אומר שאנחנו יכולים לקרוא לשני השיטות על הערך שמעבר בשביל של שיטות Area
ו String
:
אם נרצה את התוכנה, נקבל את היוצאה הבאה:
Outputarea of Circle {Radius: 10.00} is 314.16
area of Square {Width: 5.00, Height: 10.00} is 50.00
Square {Width: 5.00, Height: 10.00} is the smallest
ראינו עכשיו איך אנחנו יכולים ליצור אינטרפוסים קטנים ולבנות אחר כך אינטרפוסים גדולים כפי שנדרש. למרות שיכולנו להתחיל עם האינטרף הגדול ולהעביר אותו לכל הפונקציות שלנו, זה נחשב כהגיון לשלוח רק את האינטרף הקטן אל הפונקציה שנדרשת. זה בד "" כ מוביל לקוד ברור יותר, כי
למשל, אם נעביר Shaper
לפעולה Less
, אנחנו יכולים להניח שהיא יקחה בקשת גם את הפעולות Area
ו String
. ועדיין, בגלל שאנחנו רוצים רק לקחת את הפעולה Area
, זה מקבל את פעולת Less
בבירור, כי אנחנו יודעים שאנחנו יכולים רק לקחת את הפעולה Area
של כל אג'נדה שנעבירה לו.
הסיכוי
ראינו איך בניית ממשקים קטנים ובנייתם למעלה למקבלים גדולים מאפשרת לנו לשיתף רק מה שאנחנו צריכים לפעולה או שיטה. למדנו גם שאנחנו יכולים להרכיב את הממשקים שלנו מממשקים אחרים, כולל אלה שנוצרים מערכות אחרות, לא רק מהערכות שלנו.
אם תרצו ללמוד עוד על השפה הגונגו, בואו ובחנו את כל את סדרת ההוראות "איך לקוד בגונגו".
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-interfaces-in-go