الأساسيات

الواجهات البرمجية interface – الجزء الأول

لغة غو لا تعتبر لغة كائنية التوجه Not OOP, على عكس لغات اخرى شهيرة مثل جافا, سي بلس بلس, سي شارب وغيرها

إلا أن لغة غو تحتوي في العديد من تفاصيلها على “بعض” ميزات اللغات كائنية التوجه, فتعرفنا في دروس سابقة على البُنى structs ودوال الأساليب/الخصائص Methods

أما اليوم فدرسنا هو حول الواجهات البرمجية في لغة غو interface

الواجهة interface هي ببساطة عن “تعريف” عدة دوال تشترك لتحقيق فعل ما.

لتبسيط الأمر, مثلاً لدينا آلة غسّالة Washing Machine وهذه الآلة يمكن من خلالها تحقيق عدد من الأفعال

فيمكنها “الغسل” و”التجفيف”, وفيها زر لـ “رفع درجة حرارة”, و زر لـ “فتح الغطاء” و زر للـ”إيقاف”

ولتبسيط الأمور سنكتفي فقط بدوال لل”غسل” و الـ”إيقاف”

ويمكننا تمثيل هذه الغسّالة بواجهة كالاتي مثلاً:

type WashingMachine interface {
Wash()
Stop()
}

وهنا ينتهي دور الواجهة البرمجية interface

إذاً الواجهة فقط هدفها هو تحديد مجموعة من الدوال التي تشترك أو التي يمكن تواجدها في كائن معين.

لاحظ أنه يوجد العديد من ماركات وموديلات آلات الغسيل, ولكل ماركة/موديل منها طريقة معينة مثلاً في “التجفيف” او “الغسيل”

في الواجهات البرمجية, لايهمنا “كيفية” حصول الفعل, بل مايهم هو حصول الفعل بحد ذاته

 

ولننشئ الان عدة ماركات من آلات الغسيل

type LG struct {
}


type Whirlpool struct {
}

وهي بُنى فارغة حالياً ولايوجد ضرورة للخوض في تفاصيلها, فالهدف هو تبسيط آلية عمل الواجهات.

وفي غسّالة نوع LG لدينا دوال الأساليب Methods التالية:

func (w LG) Wash() {
	fmt.Println("LG is now washing...")
}

func (w LG) Stop() {
	fmt.Println("LG has stopped.")
}

أما في ماركة Whirlpool:

func (w Whirlpool) Wash() {
	fmt.Println("Whirlpool is now washing... ^_^")
}

func (w Whirlpool) Stop() {
	fmt.Println("Whirlpool has stopped, thanks for using us!")
}

أننا أنشئنا نوعين من الـstructs لتثميل كل ماركة

وأنشئنا لكل struct مجموعة من الmethods للغسل وللإيقاف

والان بما أن struct ماركة LG يملك نفس الدوال التي سبق أن عرفناها في الواجهة WashingMachine, فيقال أن LG struct has implemented the WashingMachine interface

أي ان الـLG struct قد أصبح “يمثّل” واجهة WashingMachine وذلك لان اصبح له دوال أساليب Methods تطابق اسم وفعل الدوال المعرفة في تلك الواجهة, ونفس الأمر تماماً ينطبق على Whirlpool struct

مع الملاحظة أن دوال الأساليب السابقة هي “خاصة” بتلك الآلات, أي أنها مثل أزرار مدمجة على الآلة نفسها

لكن لسبب ما, ترغب أنت في صناعة صندوق صغير خاص بك, يمكن من خلاله تشغيل آلات الغسيل تلك “أياً كان نوعها” علماً أن لكل نوع منها توصيلات خاصة وطريقة تشغيل وإيقاف مختلفة, ولكن بغض النظر عن “طريقة” عمل تلك الآلات, أنت تريد تحقيق فعل معيّن.

func StartWashing(wm WashingMachine) {
	fmt.Println("Control Box: Starting the washing machine....")
	wm.Wash()
}

func StopWashing(wm WashingMachine) {
	fmt.Println("Control Box: Stopping the washing machine....")
	wm.Stop()
}

 

وهذه الدوال السابقة هي خاصة بنا, لاحظ أن الباراميتر هو من نوع واجهة WashingMachine, أي أن هذه الدوال تقبل أي struct يمثّل آلة غسيل بغض النظر عن النوع او الماركة او الموديل او اي تفاصيل اخرى

والان يكون البرنامج كله كالاتي

package main

import (
"fmt"
)

// WashingMachine interface
type WashingMachine interface {
	Wash()
	Stop()
}

// an LG-type washing machine
type LG struct {
}

// a Whirlpool-type washing machine
type Whirlpool struct {
}

// LG-washing machine methods
func (w LG) Wash() {
	fmt.Println("LG is now washing...")
}

func (w LG) Stop() {
	fmt.Println("LG has stopped.")
}

// Whirlpool-washing machine methods
func (w Whirlpool) Wash() {
	fmt.Println("Whirlpool is now washing... ^_^")
}

func (w Whirlpool) Stop() {
	fmt.Println("Whirlpool has stopped, thanks for using us!")
}

// Our Control Box functions
func StartWashing(wm WashingMachine) {
	fmt.Println("Control Box: Starting the washing machine....")
	wm.Wash()
}

func StopWashing(wm WashingMachine) {
	fmt.Println("Control Box: Stopping the washing machine....")
	wm.Stop()
}

// The main program
func main() {

washingMachine1 := LG{}
washingMachine2 := Whirlpool{}
washingMachine3 := LG{}

StartWashing(washingMachine1)
StartWashing(washingMachine2)
StartWashing(washingMachine3)
}

 

النتيجة:

Control Box: Starting the washing machine....

LG is now washing...

Control Box: Starting the washing machine....

Whirlpool is now washing... ^_^

Control Box: Starting the washing machine....

LG is now washing...

في برنامجنا يوجد لدينا 3 آلات غسيل, واحدة من ماركة Whirlpool واثنتان من ماركة LG

وقمنا بتشغيلها جميعاً من خلال دالة واحدة فقط علماً ان انواعها مختلفة!

ويمكننا إيقاف أياً منها أيضاً من خلال دالة StopWashing

 

الخلاصة:

على الرغم من غرابة المثال السابق, يوجد في حياتنا اليومية العديد مما يمكننا إطلاق عليه لقب “واجهة interface”

فليس من الضروري لكل من يستخدم قطعة الكترونية ان يكون مهندس الكترونيات وان يعلم كيف يعمل الترانزستور والدارات الداخلية!, فقط عليه ان يعلم انه يوجد زر ما للتشغيل يُدعى “On” واخر للإيقاف يُدعى “Off”, اي اننا عندما نتحدث عن الواجهات فإن الهدف منها هو التركيز على الفعل behavior بحد ذاته وليس على آلية وطريقة العمل الimplementation

وكمثال عملي نستخدمه دائماً من خلال الواجهات البرمجية, هو أن بعض البرامج مثلاً “تقبل” ان تعمل على عدة انواع من قواعد البيانات (MySQL, Oracle, Redis…)

وبهذه الحالة, يقوم المبرمج بصناعة واجهة واحدة لقواعد البيانات يعرّف فيها دوال للقراءة والإدخال والحذف والتعديل CRUD

وعلى أساس هذه الواجهة يقوم باقي البرنامج, وماعلى المستخدم إلا أن يحدد نوع قاعدة البيانات الذي يرغب بالعمل عليه أو المتوفر لديه في بيئته

وبهذا نكون قد فصلنا بين “برنامجنا” وبين “الطرق المختلفة لاستخدام قواعد البيانات المختلفة” وبذلك نكون ابتعدنا عن تعقيد البرنامج spaghetti code وساعدنا على تحقيق مبدئ هام في هندسة البرمجيات وهو separation of concerns وأيضاً أحد المبادئ الأساسية في البرمجة الكائنية وهو polymorphism

 

من أهم الصعوبات التي تواجه المتعلم المستجد في لغة غو عندما يتعلق الأمر بالواجهات البرمجية, أولاً هو فهم الهدف والفوائد منها, ثانياً هو ان طريقة تحقيقها يختلف عن باقي لغات البرمجة الشهيرة, حيث انه في لغة غو فإن تحقيق الواجهات implementation يكون ضمنياً implicit على عكس لغات اخرى حيث يكون ظاهراً وواضحاً explicit

والهدف من الدرس الحالي هو تحقيق الهدف الأول, أي فهم واستيعاب فوائد الواجهات البرمجية فقط

أما في درس قادم سيكون الشرح معمقاً و “برمجياً” أكثر

وسأضع أمثلة برمجية هامة, ولن يكون هناك أمثلة عن الغسّالات ^_^

ملاحظة: يمكنك تشغيل أكواد وأمثلة لغة غو من خلال GoLang Playground أو GoPlay.Space .

نبذة عن الكاتب

Firas M. Darwish

Software Engineer, PHP/Laravel, C#, GoLang, founder of ArGoLang.com & others ..

شاركنا رأيك :)