Vivasoft-logo

[৩.৩] ইন্টারফেস (interfaces)

এখন আমরা  Go ইন্টারফেস নিয়ে আলোচনা করবো। Go তে ইন্টারফেসের ব্যবহার অন্যান্য OOP বেজড ল্যাংগুয়েজের থেকে আলাদা। তাই ইন্টারফেস সম্পর্কে আগে থেকে ধারণা থাকলেও Go এর জন্য নতুনভাবে একটা বোঝাপড়া আনা প্রয়োজন।

[৩.৩.১] ইন্টারফেস (interfaces)  কী ?

 Go তে  ইন্টারফেস  অন্যান্য ল্যাঙ্গুয়েজ থেকে ভিন্ন । Go তে  ইন্টারফেস হল কাস্টম টাইপ যা এক বা একাধিক মেথড সিগনেচারের একটি সেট নির্দিষ্ট করতে ব্যবহার করা হয় । অন্যভাবে বলা যায়, ইন্টারফেস হল অনেক গুলো মেথড সিগনেচারের সমষ্টি যেখানে বিভিন্ন টাইপের মেথড সিগনেচার থাকতে পারে । ইন্টারফেসের  ইনস্ট্যান্স তৈরি করা যায় না তবে ইন্টারফেস টাইপের ভ্যারিয়েবল তৈরি করা যায়।

যদি একটি টাইপ এমন কোনো মেথডকে ইমপ্লিমেন্ট করে যে মেথডের নাম এবং সিগনেচার আগে থেকেই কোনো   একটি ইন্টারফেসে সংজ্ঞায়িত আছে, তাহলে সেই টাইপটি সেই ইন্টারফেসটিকে  ইমপ্লিমেন্ট করে। কিছুটা এইরকম যদি কোনো কিছু হাঁসের মতো হাঁটে, হাঁসের মতো সাঁতার কাটে এবং হাঁসের মতো ঝাঁকুনি দেয়, তাহলে ঐটাও একটা হাঁস।

উদাহরণস্বরূপ, যদি Animal ইন্টারফেসের  walk()  এবং bark() দুটি মেথড সিগনেচার থাকে আর  Dog টাইপের একটি Struct যদি, ঐ দুটি মেথডকে ইমপ্লিমেন্ট করে তাহলে বলা যায় যে Dog Struct টি Animal ইন্টারফেসকে ইমপ্লিমেন্ট করেছে । 

[৩.৩.২] ডিক্লারিং ইন্টারফেস (Declaring interfaces)   

  ইন্টারফেস  ডিক্লারেশন কিছুটা Struct এর মতোই । প্রথমে টাইপ কিওয়ার্ড , তারপর ইন্টারফেসের নাম এবং ইন্টারফেস কিওয়ার্ড লিখে কার্লি ব্রাকেট দিয়ে মেথড সিগনেচারগুলো লিখতে হবে ।

নিম্নলিখিত সিনট্যাক্সের মাধ্যমে ইন্টারফেস ডিফাইন করা হয় ঃ

				
					type InterfaceName interface {
       method1(arg1 type1, arg2 type2) returnType1
       method2(arg3 type3) returnType2
       method3() returnType3
       // ...
   }

				
			

  উদাহরণস্বরূপ একটি  Animal ইন্টারফেস ডিফাইন করা যেতে পারে নিম্নলিখিত ভাবে  – 

				
					 type Animal interface {
       walk() string
       bark() string
   }

				
			

    এখানে type এবং interface হল দুইটি  keyword , Animal হল ইন্টারফেসের নাম, walk(), bark() হল ইন্টারফেসের মেথড এবং string হল মেথডের রিটার্ন  টাইপ ।  

[৩.৩.৩] ইমপ্লিমেন্টিং ইন্টারফেস (Implementing interfaces) 

    Go – তে ইন্টারফেস ইমপ্লিমেন্ট করার জন্য অন্যান্য ল্যাঙ্গুয়েজের মতো ইমপ্লিমেন্টস কিওয়ার্ড নেই কিন্তু Struct টাইপের মাধ্যমে ইন্টারফেস ইমপ্লিমেন্ট করা হয়। Go – তে inheritance সাপোর্ট করে না এর পরিবর্তে কম্পোজিশন সাপোর্ট করে। Go – তে ইন্টারফেস ইমপ্লিমেন্ট করার জন্য অবশ্যই একটি Struct বা Non-struct টাইপ থাকতে হবে, যা ইন্টারফেসের প্রত্যেকটি মেথডকে ইমপ্লিমেন্ট করবে ।

    নিম্নলিখিত সিনট্যাক্সের মাধ্যমে ইন্টারফেস ইমপ্লিমেন্ট করতে পারি –

				
					 type InterfaceName interface {
       method1(arg1 type1, arg2 type2) returnType1
       method2(arg3 type3) returnType2
       // ...
   }
   
   type TypeName struct {
       // fields
   }

   func (t TypeName) method1(arg1 type1, arg2 type2) returnType1 {
       // implementation
   }

   func (t TypeName) method2(arg3 type3) returnType2 {
       // implementation
   }

				
			

 উপরের উদাহরণে, InterfaceName হল একটি ইন্টারফেস যাকে ইমপ্লিমেন্ট করতে হবে এবং TypeName হল একটি Struct, যা ইন্টারফেসের প্রত্যেকটি মেথডকে ইমপ্লিমেন্ট করবে ।

 একটি উদাহরণ দেখলে বিষয়টি ক্লিয়ার হয়ে যাবে । নিচে একটি উদাহরণ দেওয়া হল –

				
					 package main

   import "fmt"

   type Animal interface {
       walk() string
       bark() string
   }

   type Dog struct {
       w string
       b string
   }

   func (d Dog) walk() string {
       return d.w
   }

    func (d Dog) bark() string {
       return d.b
   }

   func main() {
       var a Animal = Dog{"Dog is walking.....!!!", "Dog is barking....!!!"}
       fmt.Println("!.....Dog......!")
       fmt.Println(a.walk())
       fmt.Println(a.bark())
   }

				
			

   আউটপুট –

				
					   > go run interface.go
   !.....Dog......!
   Dog is walking.....!!!
   Dog is barking....!!!
				
			

  উপরের উদাহরণে – 

  • Animal  ইন্টারফেসের  walk(),  bark()  দুইটি মেথড এবং Dog Struct এর w, b দুইটি ফিল্ড রয়েছে। 
  • Dog Struct, walk(),  bark()  মেথড দুটিকে ইমপ্লিমেন্ট করে string রিটার্ন করেছে ফলে Animal  ইন্টারফেসের ইমপ্লিমেন্ট ও হয়ে গিয়েছে। 
  • main() ফাংশনে Animal টাইপের ভ্যারিয়েবল a তে  Dog Struct এর ফিল্ড ভ্যালু অ্যাসাইন করা হয়েছে।  

[৩.৩.৪] এম্পটি ইন্টারফেস (Empty interfaces)

   Go প্রোগ্রামিং ল্যাঙ্গুয়েজে, এম্পটি ইন্টারফেসকে “interface{}” আকারে প্রকাশ করা হয় ।  এম্পটি ইন্টারফেসে কোন মেথড সিগনেচার থাকে না, এজন্য যেকোনো টাইপের ভ্যালু এম্পটি ইন্টারফেসের সাহায্যে অ্যাক্সেস করা যায়।

 নিম্নের উদাহরণটি  লক্ষ করি –

				
					  package main

   import "fmt"

   type MyString string

   type Rectangular struct {
       width  float64
       height float64
   }

   func emptyInterface(i interface{}) {
       fmt.Printf("Value given to emptyInterface function is of type '%T' with value %v\n", i, i)
   }

   func main() {
       myString := MyString("My name is MD ABU SALMAN HOSSAIN")
       rectangular := Rectangular{5.2, 7}


       emptyInterface(myString)
       emptyInterface(rectangular)
   }

				
			

 আউটপুট – 

				
					> Go run emptyInterface.go
Value given to emptyInterface function is of type 'main.MyString' with value My name is 
MD ABU SALMAN HOSSAIN     
Value given to emptyInterface function is of type 'main.Rectangular' with value {5.2 7}

				
			

    উপরের উদাহরণে – 

  • আমরা MyString  এবং Rectangular Struct, এই দুটি কাস্টম টাইপের ইন্সট্যান্স তৈরি করেছি । 
  • একটি  emptyInterface() ফাংশন তৈরি করেছি যেটাতে এম্পটি ইন্টারফেস প্যারামিটার হিসেবে নিয়েছি । 
  • এখন  emptyInterface() ফাংশনটি যেকোনো টাইপের ভ্যালু রিসিভ করবে। 
  • আমরা main() ফাংশন থেকে ভিন্ন ভিন্ন টাইপের ভ্যালু  দিয়ে emptyInterface নামক ফাংশনকে কল করেছি এবং এটি ভিন্ন ভিন্ন টাইপেরই আউটপুট দিয়েছে । 

[৩.৩.৫] মাল্টিপল ইন্টারফেস (Multiple interfaces)

 একটি টাইপ একাধিক ইন্টারফেসকে ইমপ্লিমেন্ট করতে পারে। নিচের উদাহরণে –

  • ইন্টারফেস Shape এবং Object এ মেথড সিগনেচার যথাক্রমে Area() ও Volume() যাদের রিটার্ন টাইপ float64 । 
  • Cylinder টাইপের একটা Struct রয়েছে যার radius এবং height নামক দুটি ফিল্ড আছে। 
  • উভয় ইন্টারফেসকে Cylinder Struct ইমপ্লিমেন্ট করেছে। 
  • main() ফাংশনে Cylinder Struct এর ইনস্ট্যান্স, Shape ইন্টারফেসের ভ্যারিয়েবল shape এ রাখি।  
  • shape ইন্টারফেসের ভ্যারিয়েবলে Cylinder টাইপ অ্যাসারশন করি এবং cylinder ভ্যারিয়েবলে তা স্টোর করি। ফলে cylinder ভ্যারিয়েবল থেকে আমরা Area() ও Volume(), উভয় মেথড অ্যাক্সেস করতে পারব। 
  • এখন সেই ভ্যারিয়েবল দ্বারা Area() ও Volume() মেথডকে কল করা হয়েছে ।

নিচের  উদাহরণের মাধ্যমে দেখা যাক –

				
					  package main

   import (
       "fmt"
       "math"
   )

   type Shape interface {
       Area() float64
   }

   type Object interface {
       Volume() float64
   }

   type Cylinder struct {
       radius float64
       height float64
   }

   func (c Cylinder) Area() float64 {
       return 2 * math.Pi * c.radius * (c.radius + c.height)
   }

   func (c Cylinder) Volume() float64 {
       return math.Pi * c.radius * c.radius * c.height
   }

   func main() {
       var shape Shape = Cylinder{3.2, 4.3}
       cylinder := shape.(Cylinder)
       fmt.Println("Area of shape of interface type Shape is ", cylinder.Area())
       fmt.Println("Volume of object of interface type Object is ", cylinder.Volume())
   }



				
			

আউটপুট –

				
					  > Go run MultipleInterface.go
   Area of shape of interface type Shape is  150.79644737231007
   Volume of object of interface type Object is  138.33060772286578

				
			

[৩.৩.৬] টাইপ অ্যাসারশন (Type Assertion)

Go-তে, টাইপ অ্যাসারশন হল একটি ইন্টারফেস ভেরিয়েবলের অন্তর্নিহিত মানের সঠিক টাইপকে বের করার একটি উপায়। একটি ইন্টারফেস ভেরিয়েবলের অন্তর্নিহিত মান একটি নির্দিষ্ট টাইপের কিনা তা যাচাই করতে  টাইপ অ্যাসারশন ব্যবহার করা হয়। 

সিনট্যাক্স –

				
					value, ok := interfaceValue.(Type)
				
			

এখানে, interfaceValue হল একটি ইন্টারফেস ভেরিয়েবল এবং Type হল সেই টাইপ যাকে আমরা অ্যাসার্ট করতে চাই। value একটি ভেরিয়েবল যাতে interfaceValue.(Type) এর মান স্টোর করতে  এবং ok একটি বুলিয়ান ভেরিয়েবল যাতে  অ্যাসার্ট টাইপ true অথবা false কিনা সেই মান স্টোর করতে ডিফাইন করা হয়েছে।

 চলুন একটি উদাহরন দেখে নেওয়া যাক।

				
					 package main

   import (
       "fmt"
       "math"
   )

   type Shape interface {
       Area() float64
   }

   type Object interface {
       Volume() float64
   }
   type Cylinder struct {
       radius float64
       height float64
   }

   func (c Cylinder) Area() float64 {
       return 2 * math.Pi * c.radius * (c.radius + c.height)
   }

   func (c Cylinder) Volume() float64 {
       return math.Pi * c.radius * c.radius * c.height
   }

   func main() {
       var s Shape = Cylinder{3.2, 4.3}
       c, ok := s.(Cylinder)
       if ok {
           fmt.Println("Area of shape of interface type Shape is ", c.Area())
           fmt.Println("Volume of object of interface type Object is ", c.Volume())
       } else {
           fmt.Println("It’s not a cylinder type ")
       }
   }

				
			

 আউটপুট –

				
					

   > Go run MultipleInterface.go
   Area of shape of interface type Shape is  150.79644737231007
   Volume of object of interface type Object is  138.33060772286578



				
			

এখানে,s হল Shape ইন্টারফেসের একটি ভেরিয়েবল যাতে Cylinder Struct কে আসাইন করা হয়েছে। তারপর s.(Cylinder) কে c ভ্যারিয়েবলে স্টোর করা হয়েছে অর্থাৎ এখানে Cylinder Struct এর টাইপ অ্যাসার্ট করা হয়েছে s এ এবং তা c ভেরিয়েবলে এবং অ্যাসার্ট টাইপ true  না false সেটা ok ভেরিয়েবলে  রাখা হয়েছে। যদি true হয় তাহলে  আমরা c- এ Area() এবং Volume() মেথড ব্যবহার করতে পারি কারণ c হল Cylinder টাইপের একটি ইন্সট্যান্স, যা Cylinder মেথডগুলো ইমপ্লিমেন্ট করতে পারবে আর যদি false হয় তাহলে প্রিন্ট হবে “It’s not a cylinder type”।