15 فکت جذاب در مورد اینترفیس ها (Interfaces) در گولنگ

اینترفیس‌ها (Interfaces) یکی از ویژگی‌های کلیدی زبان Go هستند که به انعطاف‌پذیری و توسعه‌پذیری کد کمک می‌کنند. برخلاف زبان‌های شیءگرا که اینترفیس‌ها را به وراثت گره می‌زنند، Go از یک رویکرد مبتنی بر رفتار استفاده می‌کند. در این مقاله، ۱۵ نکته جذاب و مهم در مورد اینترفیس‌های Go را بررسی می‌کنیم که درک عمیق‌تری از این مفهوم به شما می‌دهد.


1. Implicit Implementation

تو اکثر زبان های برنامه نویسی مثل پی اچ پی، پایتون و..، شما باید مشخص کنید که مثلا این کلاس داره از این اینترفیس استفاده میکنه که اصولا از کلمه "implementation" استفاده میکنید. اما در گولنگ این شکلی نیست، این اتفاق به صورت اتوماتیک می افته و گولنگ خودش متوجه میشه که از کدوم اینترفیس داره استفاده میشه. حالا سوال پیش میاد از کجا میخواد بفهمه؟ گولنگ وقتی ببینه که struct شما متد های یک اینترفیس به صورت کامل پیاده سازی کردید، متوجه این داستان میشه.



2. Interfaces Can Be Satisfied by Any Type

در گولنگ اینترفیس ها میتونن تایپ های ساده ای مثل string، int و struct های شما میتونن از اینترفیس ها استفاده کنند (اگه متد ها پیاده سازی شده باشه). این ویژگی باعث می‌شود که اینترفیس ها بسیار انعطاف‌پذیر و قدرتمند باشند.


3. Empty Interface: The Ultimate Generalization

این قابلیت به شما این امکان میدهد که هر تایپی پیاده سازی کنید. با استفاده از "interface{}"، شما یک اینترفیس خالی درست کنید و هر نوع تایپی بپذیرید.


4. Interfaces Are Lightweight

بله، اینترفیس‌ها در Go به‌صورت یک جفت پیاده‌سازی می‌شوند: یک مقدار مشخص (concrete value) و یک اشاره‌گر به اطلاعات نوع آن. این طراحی سبک باعث می‌شود که اینترفیس‌ها از نظر حافظه بهینه و از نظر اجرای متدها سریع باشند.


5. Decoupling with Interface Composition

گولنگ استفاده از اینترفیس‌های کوچک و متمرکز را تشویق می‌کند. اینترفیس‌های بزرگ‌تر را می‌توان با ترکیب اینترفیس‌های کوچک‌تر ساخت که منجر به کدی ماژولارتر و قابل نگهداری‌تر می‌شود:

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type ReadWriter interface {
    Reader
    Writer
}


6. Interfaces Allow for Dependency Injection

اینترفیس‌ها امکان جایگزینی آسان پیاده‌سازی‌های مشخص را فراهم می‌کنند و این باعث می‌شود که بتوان رفتارهای مختلف، مانند ماک کردن آبجکت ها (mock)، را به‌راحتی در تست‌ها تزریق کرد. این ویژگی کد را قابل تست‌تر و نگهداری‌پذیرتر می‌کند:

package main

import "fmt"

// MessageSender اینترفیس ارسال پیام
type MessageSender interface {
    SendMessage(to, message string) error
}

// EmailSender پیاده‌سازی واقعی ارسال ایمیل
type EmailSender struct{}

func (e EmailSender) SendMessage(to, message string) error {
    fmt.Printf("Sending email to %s: %s", to, message)
    return nil
}

// MockSender پیاده‌سازی ماک برای تست
type MockSender struct{}

func (m MockSender) SendMessage(to, message string) error {
    fmt.Printf("Mock send to %s: %s", to, message)
    return nil
}

// تابعی که از اینترفیس استفاده می‌کند
func NotifyUser(sender MessageSender, user string) {
    sender.SendMessage(user, "Hello from our service!")
}

func main() {
    realSender := EmailSender{}
    mockSender := MockSender{}

    fmt.Println("Using real sender:")
    NotifyUser(realSender, "user@example.com")

    fmt.Println("Using mock sender (for test):")
    NotifyUser(mockSender, "test@example.com")
}


7. Interfaces Enable Polymorphism

با استفاده از اینترفیس‌ها، گولنگ به پلی‌مورفیسم دست می‌یابد، به این معنا که انواع مختلف می‌توانند به عنوان یک نوع اینترفیس مشترک در نظر گرفته شوند. این امکان باعث می‌شود که بتوان کدی نوشت که بدون تغییر با انواع مختلف کار کند، که در نتیجه طراحی‌های منعطف و قابل گسترش ایجاد می‌شود:

package main

import "fmt"

// PaymentProcessor اینترفیس برای پردازش پرداخت
type PaymentProcessor interface {
    ProcessPayment(amount float64)
}

// CreditCard پردازش پرداخت با کارت اعتباری
type CreditCard struct {
    CardNumber string
}

func (c CreditCard) ProcessPayment(amount float64) {
    fmt.Printf("Processing credit card payment of $%.2f from card %s", amount, c.CardNumber)
}

// PayPal پردازش پرداخت با پی‌پال
type PayPal struct {
    Email string
}

func (p PayPal) ProcessPayment(amount float64) {
    fmt.Printf("Processing PayPal payment of $%.2f from email %s", amount, p.Email)
}

// ProcessTransaction پردازش تراکنش با اینترفیس PaymentProcessor
func ProcessTransaction(p PaymentProcessor, amount float64) {
    p.ProcessPayment(amount)
}

func main() {
    creditCard := CreditCard{CardNumber: "1234-5678-9012-3456"}
    payPal := PayPal{Email: "user@example.com"}

    fmt.Println("Using Credit Card:")
    ProcessTransaction(creditCard, 100.0)

    fmt.Println("Using PayPal:")
    ProcessTransaction(payPal, 50.0)
}


8. Dynamic Type Identification with Type Assertions

با استفاده از اینترفیس ها میتونید نوع های داینامیک داشته باشید،  این قابلیت روش‌های قدرتمندی را برای کار با انواع دینامیک فراهم می‌کند:

package main

import "fmt"

func main() {
    var i interface{} = "Hello, Go!"

    // Type assertion: استخراج مقدار رشته‌ای از اینترفیس
    str, ok := i.(string)
    if ok {
       fmt.Println("Extracted string:", str)
    } else {
       fmt.Println("Type assertion failed!")
    }
}

9. Type Switches for Multiple Types Handling

در گولنگ شما میتونید با استفاده از اینترفیس و سوپیچ کیس (Switch/Case)، بیاید تایپ یک متغیر چک کنید و براساس تایپ اون عملیاتی انجام بدید:

func doSomething(i interface{}) {
    switch v := i.(type) {
    case int:
       fmt.Println("Integer:", v)
    case string:
       fmt.Println("String:", v)
    default:
       fmt.Println("Unknown type")
    }
}

10. Interfaces Promote Decoupling and Reusability

با برنامه‌نویسی بر اساس اینترفیس‌ها به جای پیاده‌سازی‌های خاص، کد شما ماژولارتر و راحت‌تر قابل تغییر خواهد بود. این جداسازی (decoupling) منجر به استفاده مجدد بهتر از کد و آسانی در اعمال تغییرات آینده می‌شود:

package main

import "fmt"

// Database اینترفیس دیتابیس
type Database interface {
    Connect()
}

// MySQLDatabase پیاده‌سازی MySQL
type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL database...")
}

// PostgreSQLDatabase پیاده‌سازی PostgreSQL
type PostgreSQLDatabase struct{}

func (db PostgreSQLDatabase) Connect() {
    fmt.Println("Connecting to PostgreSQL database...")
}

// تابعی که از اینترفیس استفاده می‌کند و وابسته به پیاده‌سازی خاصی نیست
func InitializeDatabase(db Database) {
    db.Connect()
}

func main() {
    mysqlDB := MySQLDatabase{}
    postgresDB := PostgreSQLDatabase{}

    fmt.Println("Using MySQL:")
    InitializeDatabase(mysqlDB)

    fmt.Println("Using PostgreSQL:")
    InitializeDatabase(postgresDB)
}

11. Interfaces Can Be Used to Define Behavior Across Unrelated Types

در گو، اینترفیس‌ها به جای وراثت بر رفتار تمرکز دارند. این ویژگی باعث می‌شود بتوان اینترفیس‌هایی تعریف کرد که انواع کاملاً رفتار های نامرتبط را پوشش دهند و رفتارهای مشترک را در بین آن‌ها اعمال کنند.

12. Reflection and Interfaces

در Go، پکیج "reflect" امکان بررسی و تغییر اینترفیس‌ها در زمان اجرا را فراهم می‌کند. این قابلیت برای تکنیک‌های پیشرفته مانند سریال‌سازی، دی‌سریال‌سازی، و فراخوانی متدها به‌صورت دینامیک بسیار مفید است:

package main

import (
    "fmt"
    "reflect"
)

func inspect(value interface{}) {
    v := reflect.ValueOf(value)
    t := reflect.TypeOf(value)

    fmt.Println("Type:", t)
    fmt.Println("Value:", v)
}

func main() {
    inspect(42)
    inspect("Hello, Go!")
    inspect(3.14)
}

13. Interfaces in Go Are Not Tied to Object-Oriented Programming

در بسیاری از زبان‌ها، اینترفیس‌ها به شدت با برنامه‌نویسی شیءگرا (OOP) گره خورده‌اند، اما در گولنگ، اینترفیس‌ها به جای ایجاد سلسله‌مراتب کلاس‌ها، صرفاً رفتار را تعریف می‌کنند. این ویژگی باعث می‌شود اینترفیس‌های گو ابزاری انعطاف‌پذیر باشند که در پارادایم‌های مختلف از جمله برنامه‌نویسی تابعی (Functional) و رویه‌ای (Procedural) نیز قابل استفاده هستند.

14. Interfaces Can Be Used to Implement the Null Object Pattern

در گو، می‌توان از اینترفیس‌ها برای پیاده‌سازی الگوی Null Object استفاده کرد. در این الگو، به جای مقدار nil، یک نوعی که متدهای مورد نیاز را پیاده‌سازی می‌کند ولی کاری انجام نمی‌دهد به عنوان مقدار پیش‌فرض قرار داده می‌شود. این کار باعث می‌شود که نیازی به بررسی nil در کد نهایی نداشته باشیم و از مشکلات مرتبط با مقدار nil جلوگیری شود:

type Logger interface {
    Log(message string)
}

type NullLogger struct{}

func (n NullLogger) Log(message string) {
    // No-op
}

15. Interfaces as Contracts

در گولنگ، زمانی که یک اینترفیس تعریف می‌کنید، در واقع یک قرارداد (Contract) ایجاد می‌کنید که هر نوعی که این قرارداد را رعایت کند، می‌تواند در بخش‌های مختلف برنامه استفاده شود. این کار تضمین می‌کند که مجموعه‌ای از متدهای مشخص در دسترس هستند، که باعث هماهنگی و انسجام در تعامل بین بخش‌های مختلف کد می‌شود.

0 🔥
1 🎉
0 😮
0 👍
0 💜
0 👏
میلاد خسروی
نویسنده کد نیوز

برنامه نویس فان | Fun Developer یک آدم ساده که عاشق برنامه نویسی و کد زدنه :) تلاش میکنه تا به بقیه کمک کنه. توسعه دهنده هسته لاراول و فضای اوپن سورس. فاندر پرانتز و کد نیوز.

0+ نظر

برای ثبت نظر ابتدا ورود کنید.

0 نظر

    اولین نفر باش که نظر ثبت میکنی :) یعنی یه کامنت به ما نمیرسه 😁