برنامه نویسی شی گرای سومین نسل از الگوهای اصلی برنامه نویسی است. در توضیحات فصل اول گفته شد که #F یک زبان تابع گرا است ولی این بدان معنی نیست که #F از مفاهیمی نظیر کلاس و یا interface پشتیبانی نکند. برعکس در #F امکان تعریف کلاس و interface و هم چنین پیاده سازی مفاهیم شی گرایی وجود دارد.
*با توجه به این موضوع که فرض است دوستان با مفاهیم شی گرایی آشنایی دارند از توضیح و تشریح این مفاهیم خودداری میکنم.
Classes
کلاس چارچوبی از اشیا است برای نگهداری خواص(Properties) و رفتار ها(Methods) و رخدادها(Events). کلاس پایه ایترین مفهوم در برنامه نویسی شی گراست. ساختار کلی تعربف کلاس در #F به صورت زیر است:
همان طور که در ساختار بالا میبینید مفاهیم access-modifier و inherit و constructor هم در #F وجود دارد.
انواع access-modifier در #F
کلاس بالا دارای یک سازنده است که دو پارامتر ورودی میگیرد. کلمه end به معنای انتهای کلاس است. برای استفاده کلاس باید به صورت زیر عمل کنید:
توابع و خواص در کلاس ها
برای تعریف خاصیت در #F باید از کلمه کلیدی member استفاده کنید. در مثال بعدی برای کلاس بالا تابع و خاصیت تعریف خواهیم کرد.
کلاس بالا دارای سه خاصیت به نامهای Number و Name و Amount است و دو تابع به نامهای Deposit و Withdraw دارد. اما x استفاده شده قبل از هر member به معنی this در #C است. در #F شما برای اشاره به شناسههای یک محدوده خودتون باید یک نام رو برای اشاره گر مربوطه تعیین کنید.
استفاده از کلمه do
در #F زمانی که قصد داشته باشیم در بعد از وهله سازی از کلاس و فراخوانی سازنده، عملیات خاصی انجام شود(مثل انجام برخی عملیات متداول در سازندههای کلاسهای دات نت) باید از کلمه کلیدی do به همراه یک بلاک از کد استفاده کنیم.
یک مثال در این زمینه:
در مثال بالا دو عبارت do یکی به صورت static و دیگری به صورت غیر static تعریف شده اند. استفاده از do به صورت غیر static این امکان را به ما میدهد که بتوانیم به تمام شناسهها و توابع تعریف شده در کلاس استفاده کنیم ولی do به صورت static فقط به خواص و توابع از نوع static در کلاس دسترسی دارد.
خروجی مثال بالا:
خواص static:
برای تعریف خواص به صورت استاتیک مانند #C از کلمه کلیدی static استفاده کنید.مثالی در این زمینه:
SomeStaticMethod به صورت استاتیک تعریف شده در حالی که x.Prop به صورت غیر استاتیک. دسترسی به متدها یا خواص static باید بدون وهله سازی از کلاس انجام بگیرد در غیر این صورت با خطای کامپایلر روبرو خواهید شد.
روش استفاده درست:
متدهای get , set در خاصیت ها:
همانند #C و سایر زبانهای دات نت امکان تعریف متدهای get و set برای خاصیتهای یک کلاس وجود دارد.
ساختار کلی:
مثالی در این زمینه:
کد متناظر در #C:
یا به صورت:
Interface ها
اینترفیس به تمامی خواص و توابع عمومی اشئایی که آن را پیاده سازی کرده اند اشاره میکند. (توضیحات بیشتر (^ ) و (^ ))ساختار کلی برای تعریف آن به صورت زیر است:
مثال:
استفاده از حرف I برای شروع نام اینترفیس طبق قوانین تعریف شده (اختیاری) برای نام گذاری است.
نکته: در هنگام تعریف توابع و خاصیت در interfaceها باید از کلمه abstract استفاده کنیم. هر کلاسی که از یک یا چند تا اینترفیس ارث ببرد باید تمام خواص و توابع اینتریسها را پیاده سازی کند. در مثال بعدی کلاس SomeClass1 اینترفیس بالا را پیاده سازی میکند. دقت کنید که کلمه this توسط من به عنوان اشاره گر به اشیای کلاس تعیین شده و شما میتونید از هر کلمه یا حرف دیگری استفاده کنید.
نکته مهم: اگر قصد فراخوانی متد Print را در کلاس بالا دارید نمیتونید به صورت مستقیم متد بالا را فراخوانی کنید. بلکه حتما باید کلاس به
اینترفیس مربوطه cast شود.
روش نادرست:
روش درست:
برای عملیات cast ازاستفاده کنید.
در مثال بعدی کلاسی خواهیم داشت که از سه اینترفیس ارث میبرد. در نتیجه باید تمام متدهای هر سه اینترفیس را پیاده سازی کند.
فراخوانی این متدها نیز به صورت زیر خواهد بود:
کلاسهای Abstract
#F از کلاسهای abstract هم پشتیبانی میکند. اگر با کلاسهای abstract در #C آشنایی ندارید میتونید مطالب مورد نظر رو در (^ ) و (^ ) مطالعه کنید. به صورت خلاصه کلاسهای abstract به عنوان کلاسهای پایه در برنامه نویسی شی گرا استفاده میشوند. این کلاسها دارای خواص و متدهای پیاده سازی شده و نشده هستند. خواص و متد هایی که در کلاس پایه abstract پیاده سازی نشده اند باید توسط کلاس هایی که از این کلاس پایه ارث میبرند حتما پیاده سازی شوند.
ساختار کلی تعریف کلاسهای abstract:
در #F برای این که مشخص کنیم که یک کلاس abstract است حتما باید [<AbstractClass>] در بالای کلاس تعریف شود.
کلاس بالا تعریفی از کلاس abstract است که سه خصوصیت abstract دارد (برای
تعیین خصوصیتها و متد هایی که در کلاس پایه پیاده سازی نمیشوند از کلمه
کلیدی abstract در هنگام تعریف آنها استفاده میکنیم). حال دو کلاس ایجاد میکنیم که این کلاس پایه را پیاده سازی کنند.
#1 کلاس اول
#2 کلاس دوم
Structures
structureها در #F دقیقا معال struct در #C هستند. توضیحات بیشتر درباره struct در #C (^ ) و (^ )). اما به طور خلاصه باید ذکر کنم که strucureها تقریبا دارای مفهوم کلاس هستند با اندکی تفاوت که شامل موارد زیر است:
یک نکته مهم هنگام کار با structها در #F این است که امکان استفاده از let
و Binding در structها وجود ندارد. به جای آن باید از val استفاده کنید.
تفاوت اصلی بین val و let در این است که هنگام تعریف
شناسه با val امکان مقدار دهی اولیه به شناسه وجود ندارد. در مثال بالا
مقادیر برای x و y و z برابر 0.0 است که توسط کامپایلر انجام میشود. در
ادامه یک struct به همراه سازنده تعریف میکنیم:
توسط سازنده struct بالا مقادیر اولیه x و y دریافت میشود به متغیرهای متناظر انتساب میشود.
در پایان یک مثال مشترک رو در #C و #F پیاده سازی میکنیم:
*با توجه به این موضوع که فرض است دوستان با مفاهیم شی گرایی آشنایی دارند از توضیح و تشریح این مفاهیم خودداری میکنم.
Classes
کلاس چارچوبی از اشیا است برای نگهداری خواص(Properties) و رفتار ها(Methods) و رخدادها(Events). کلاس پایه ایترین مفهوم در برنامه نویسی شی گراست. ساختار کلی تعربف کلاس در #F به صورت زیر است:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] = [ class ] [ inherit base-type-name(base-constructor-args) ] [ let-bindings ] [ do-bindings ] member-list ... [ end ] type [access-modifier] type-name1 ... and [access-modifier] type-name2 ... ...
انواع access-modifier در #F
- public : دسترسی برای تمام فراخوانها امکان پذیر است
- internal : دسترسی برای تمام فراخوان هایی که در همین assembly هستند امکان پذیر است
- private : دسترسی فقط برای فراخوانهای موجود در همین ماژول امکان پذیر است
نکته : protected access modifier در #F پشتیبانی نمیشود.
مثالی از تعریف کلاس:
type Account(number : int, name : string) = class let mutable amount = 0m end
let myAccount = new Account(123456, "Masoud")
برای تعریف خاصیت در #F باید از کلمه کلیدی member استفاده کنید. در مثال بعدی برای کلاس بالا تابع و خاصیت تعریف خواهیم کرد.
type Account(number : int, name: string) = class let mutable amount = 0m member x.Number = number member x.Name= name member x.Amount = amount member x.Deposit(value) = amount <- amount + value member x.Withdraw(value) = amount <- amount - value end
open System type Account(number : int, name: string) = class let mutable amount = 0m member x.Number = number member x.Name= name member x.Amount = amount member x.Deposit(value) = amount <- amount + value member x.Withdraw(value) = amount <- amount - value end
let masoud= new Account(12345, "Masoud") let saeed = new Account(67890, "Saeed") let transfer amount (source : Account) (target : Account) = source.Withdraw amount target.Deposit amount let printAccount (x : Account) = printfn "x.Number: %i, x.Name: %s, x.Amount: %M" x.Number x.Name x.Amount let main() = let printAccounts() = [masoud; saeed] |> Seq.iter printAccount printfn "\nInializing account" homer.Deposit 50M marge.Deposit 100M printAccounts() printfn "\nTransferring $30 from Masoud to Saeed" transfer 30M masoud saeed
printAccounts() printfn "\nTransferring $75 from Saeed to Masoud" transfer 75M saeed masoud printAccounts() main()
در #F زمانی که قصد داشته باشیم در بعد از وهله سازی از کلاس و فراخوانی سازنده، عملیات خاصی انجام شود(مثل انجام برخی عملیات متداول در سازندههای کلاسهای دات نت) باید از کلمه کلیدی do به همراه یک بلاک از کد استفاده کنیم.
open System open System.Net type Stock(symbol : string) = class let mutable _symbol = String.Empty do //کد مورد نظر در این جا نوشته میشود end
open System type MyType(a:int, b:int) as this = inherit Object() let x = 2*a let y = 2*b do printfn "Initializing object %d %d %d %d %d %d" a b x y (this.Prop1) (this.Prop2) static do printfn "Initializing MyType." member this.Prop1 = 4*x member this.Prop2 = 4*y override this.ToString() = System.String.Format("{0} {1}", this.Prop1, this.Prop2) let obj1 = new MyType(1, 2)
خروجی مثال بالا:
Initializing MyType. Initializing object 1 2 2 4 8 16
برای تعریف خواص به صورت استاتیک مانند #C از کلمه کلیدی static استفاده کنید.مثالی در این زمینه:
type SomeClass(prop : int) = class member x.Prop = prop static member SomeStaticMethod = "This is a static method" end
let instance = new SomeClass(5);; instance.SomeStaticMethod;; output: stdin(81,1): error FS0191: property 'SomeStaticMethod' is static.
SomeClass.SomeStaticMethod;; (* invoking static method *)
همانند #C و سایر زبانهای دات نت امکان تعریف متدهای get و set برای خاصیتهای یک کلاس وجود دارد.
ساختار کلی:
member alias.PropertyName with get() = some-value and set(value) = some-assignment
type MyClass() = class let mutable num = 0 member x.Num with get() = num and set(value) = num <- value end;;
public int Num { get{return num;} set{num=value;} }
type MyClass() = class let mutable num = 0 member x.Num with get() = num and set(value) = if value > 10 || value < 0 then raise (new Exception("Values must be between 0 and 10")) else num <- value end
Interface ها
اینترفیس به تمامی خواص و توابع عمومی اشئایی که آن را پیاده سازی کرده اند اشاره میکند. (توضیحات بیشتر (^ ) و (^ ))ساختار کلی برای تعریف آن به صورت زیر است:
type type-name = interface inherits-decl member-defns end
type IPrintable = abstract member Print : unit -> unit
نکته: در هنگام تعریف توابع و خاصیت در interfaceها باید از کلمه abstract استفاده کنیم. هر کلاسی که از یک یا چند تا اینترفیس ارث ببرد باید تمام خواص و توابع اینتریسها را پیاده سازی کند. در مثال بعدی کلاس SomeClass1 اینترفیس بالا را پیاده سازی میکند. دقت کنید که کلمه this توسط من به عنوان اشاره گر به اشیای کلاس تعیین شده و شما میتونید از هر کلمه یا حرف دیگری استفاده کنید.
type SomeClass1(x: int, y: float) = interface IPrintable with member this.Print() = printfn "%d %f" x y
روش نادرست:
let instance = new SomeClass1(10,20) instance.Print//فراخوانی این متد باعث ایجاد خطای کامپایلری میشود.
let instance = new SomeClass1(10,20) let instanceCast = instance :> IPrintable// استفاده از (<:) برای عملیات تبدیل کلاس به اینترفیس instanceCast.Print
در مثال بعدی کلاسی خواهیم داشت که از سه اینترفیس ارث میبرد. در نتیجه باید تمام متدهای هر سه اینترفیس را پیاده سازی کند.
type Interface1 = abstract member Method1 : int -> int type Interface2 = abstract member Method2 : int -> int type Interface3 = inherit Interface1 inherit Interface2 abstract member Method3 : int -> int type MyClass() = interface Interface3 with member this.Method1(n) = 2 * n member this.Method2(n) = n + 100 member this.Method3(n) = n / 10
let instance = new MyClass() let instanceToCast = instance :> Interface3 instanceToCast.Method3 10
#F از کلاسهای abstract هم پشتیبانی میکند. اگر با کلاسهای abstract در #C آشنایی ندارید میتونید مطالب مورد نظر رو در (^ ) و (^ ) مطالعه کنید. به صورت خلاصه کلاسهای abstract به عنوان کلاسهای پایه در برنامه نویسی شی گرا استفاده میشوند. این کلاسها دارای خواص و متدهای پیاده سازی شده و نشده هستند. خواص و متد هایی که در کلاس پایه abstract پیاده سازی نشده اند باید توسط کلاس هایی که از این کلاس پایه ارث میبرند حتما پیاده سازی شوند.
ساختار کلی تعریف کلاسهای abstract:
[<AbstractClass>] type [ accessibility-modifier ] abstract-class-name = [ inherit base-class-or-interface-name ] [ abstract-member-declarations-and-member-definitions ] abstract member member-name : type-signature
[<AbstractClass>] type Shape(x0 : float, y0 : float) = let mutable x, y = x0, y0 let mutable rotAngle = 0.0 abstract Area : float with get abstract Perimeter : float with get abstract Name : string with get
#1 کلاس اول
type Square(x, y,SideLength) = inherit Shape(x, y)
override this.Area = this.SideLength * this.SideLength override this.Perimeter = this.SideLength * 4. override this.Name = "Square"
type Circle(x, y, radius) = inherit Shape(x, y)
let PI = 3.141592654 member this.Radius = radius override this.Area = PI * this.Radius * this.Radius override this.Perimeter = 2. * PI * this.Radius
structureها در #F دقیقا معال struct در #C هستند. توضیحات بیشتر درباره struct در #C (^ ) و (^ )). اما به طور خلاصه باید ذکر کنم که strucureها تقریبا دارای مفهوم کلاس هستند با اندکی تفاوت که شامل موارد زیر است:
- structureها از نوع مقداری هستند و این بدین معنی است مستقیما درون پشته ذخیره میشوند.
- ارجاع به structureها از نوع ارجاع با مقدار است بر خلاف کلاسها که از نوع ارجاع به منبع هستند.(^ )
- structureها دارای خواص ارث بری نیستند.
- عموما از structure برای ذخیره مجموعه ای از دادهها با حجم و اندازه کم استفاده میشود.
ساختار کلی تعریف structure
[ attributes ] type [accessibility-modifier] type-name = struct type-definition-elements end //یا به صورت زیر [ attributes ] [<StructAttribute>] type [accessibility-modifier] type-name = type-definition-elements
type Point3D = struct val x: float val y: float val z: float end
type Point2D = struct val X: float val Y: float new(x: float, y: float) = { X = x; Y = y } end
در پایان یک مثال مشترک رو در #C و #F پیاده سازی میکنیم: