مقدمه:
مهمترین علت وجودی Object Oriented مبحث میراث بری ( Inheritence * ) هست. به عبارتی هر کلاسی به غیر از کلاس Object مشتق شده از کلاس دیگری خواهد بود. بنابراین در صورتیکه SuperClass * برای یک کلاس تعریف نشود، همان کلاس Object مد نظر خواهد بود.
هر کلاس خواص public و protected کلاس بالادستی خود را دارا خواهد بود به علاوه اینکه می تواند دارای خواص دیگر منحصر به فردی باشد که باز هم ممکن است این خواص را به SubClass های خود بدهد یا ندهد. به طور مثال در مورد کلاس وسایل نقلیه ( Vehicle ) می توان عناصر mPosition, mType, mSpeed, ... را جزء خواصی دانست که همه Vehicle ها از جمله Car, Boat, Airplane آنرا تعریف می کنند. اما کلاس Boat دارای المان دیگری مانند « لنگر » خواهد بود که Car و Airplane آنرا ندارند. کلاس Boat هم می تواند دارای زیر کلاسهای دیگری باشد که همه آنها « لنگر » را تعریف کنند، حال اگر « لنگر » به عنوان private تعریف شده باشد، با اینکه در زیر کلاس Boat به آن دسترسی وجود ندارد اما داخل زیر کلاس Boat خواهد بود.
آموزش:
برای محدود کردن میراث بری کلاسها از modifier های public, private, protected, default که در قسمت قبل توضیح داده شد بسیار استفاده می کنیم. فرض کنیم می خواهیم دیاگرام زیر را که به آن Class Hierachy * می گویند با استفاده از برنامه نویسی بوجود آوریم.
کلاسهای زیر را مشاهده کنید، سورس کلاسها نسبت به قسمت قبل تغییر یافته است و چند کلاس هم اضافه شده است. این کلاسها کاربرد صحیحی در عمل ندارند و به مقاصد آموزشی اینگونه طراحی شده اند ولی نزدیک به ماهیت هر کلاس. توجه کنید که برای مفهوم تر شدن کدها کلاسها به package دیگری انتقال یافتند. برای اینکار می توانید بر روی src در پروژه خود Right-Click کرده و New->Package را انتخاب کنید و بنویسید com.uncocoder.course.lessons.sample1 ، با اینکار یک package جدید ساخته می شود. حالا می تواند با Drag & Drop موس ، Class های خود را به این Package انتقال دهید.
کلاس Vehicle:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 import android.util.Log;
04 import com.uncocoder.course.lessons.NetworkManager;
05
06 public class Vehicle ..
07
08 public final static int TYPE_UNDEFINED = 0;
09 public final static int TYPE_CAR = 1;
10 public final static int TYPE_BOAT = 2;
11 public final static int TYPE_AIRPLANE = 3;
12
13 public static int mVehicleCount = 0;
14
15 public int mWeight = 0;
16 public float mSpeed = 0;
17
18 protected int mType = 0;
19
20
21
22 public Vehicle() ..
23 Vehicle.mVehicleCount++;
24 }
25
26
27
28 public Vehicle(float speed, int weight) ..
29 Log.i("LOG", "Vehicle Created with Speed: " + speed + ", Weight: " + weight);
30 mSpeed = speed;
31 mWeight = weight;
32 Vehicle.mVehicleCount++;
33 }
34
35
36
37 public void setType(int type) ..
38 if (type == TYPE_CAR) ..
39 Log.i("LOG", "Vehicle is Car");
40
41 } else if (type == TYPE_BOAT) ..
42 Log.i("LOG", "Vehicle is Boat");
43
44 } else if (type == TYPE_AIRPLANE) ..
45 Log.i("LOG", "Vehicle is Airplane");
46
47 } else if (type == TYPE_UNDEFINED) ..
48 Log.i("LOG", "Type Undefined, Define a Valid Type");
49 return;
50
51 } else ..
52 Log.i("LOG", "Use Const Values to Define a Type");
53 return;
54 }
55
56 mType = type;
57 Log.i("LOG", "Vehicle Type Set!");
58 }
59
60
61
62 public int getType() ..
63 Log.i("LOG", "Vehicle Type is: " + mType);
64 return mType;
65 }
66
67
68
69 public void turnOn() ..
70 Log.i("LOG", "Vehicle Turned On");
71 }
72
73
74
75 public void turnOff() ..
76 Log.i("LOG", "Vehicle Turned Off");
77 }
78
79
80
81 public void startMove() ..
82 Log.i("LOG", "Vehicle Starts Moving");
83 }
84
85
86
87 public void sendNetworkData() ..
88 Log.i("LOG", "Vehicle Networking");
89 NetworkManager.sendVehicle(this);
90 }
91 }
خط 8 تا 11 : تعدادی ثابت به منظور مشخص کردن تایپ هر کلاس تعیین شده است. تایپ کلاس کمک می کنه که به راحتی نوع اشیاء را مورد بررسی قرار دهیم. البته راههای دیگری نیز وجود دارد ولی فعلاً به این روش بسنده می کنیم
خط 13 : یک static به منظور نگه داشتن تعداد Vehicle های ساخته شده تعریف کرده ایم. همانطور که در خطوط 23 و 32 مشخص است به این مقدار یکی اضافه می کنیم. چون برای ساختن یک Vehicle حتماً از یکی از Constructor ها باید استفاده شود.
خط 15و 16: دو فیلد public تعریف شده اند که از هر کلاسی می توان به آنها دسترسی پیدا کرد.
خط 18: یک فیلد protected تعریف شده است بنابراین از این کلاس و سایر subclass های این کلاس می توان به آن دسترسی پیدا کرد.
خط 22: یک Constructor بدون ورودی تعریف شده است
خط 28: یک Constructor با دو ورودی تعریف شده است که ابتدا پیغامی چاپ می کند و سپس مقادیر دو فیلد public را مقدار دهی می کند.
خط 37: یک Setter برای mType تعریف شده است چرا که mType فقط از زیرکلاسها قابل دسترسی است و با اینکار setType که public است از همه جا قابل دسترسی است. اما قبل از Set کردن مقدار mType پیغامهای مناسب خارج می کند
خط 62: یک Getter برای mType تعریف شده است. با توجه به تعریف Setter و Getter برای mType پس Encapsulate شده است. با استفاده از Getter مقدار متغیر mType که protected است را می توان از هر کلاسی خواند چرا که Getter بصورت public تعریف شده است
خط 69 تا 90: تعدادی متد تعریف شده اند که همه public هستند و از هر کلاسی قابل دسترسی هستند.
کلاس Car:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03
04 public class Car extends Vehicle ..
05
06 public int mWheelsCount = 4;
07
08
09
10 public Car() ..
11 mType = Vehicle.TYPE_CAR;
12 }
13 }
خط 4: کلاس Car از Vehicle گرفته شده است و می گوییم Car یک Vehicle است. در اینصورت تمامی Field ها، Method ها، و ... به Car به ارث می رسد چه public باشند چه protected، اما خواص private فقط برای همان کلاس حفظ خواهد شد و به زیر کلاسها به ارث نخواهد رسید.
خط 6: علاوه بر member های موجود در Vehicle می خواهیم بک فیلد جدید تعریف کنیم که تعداد چرخهای یک ماشین را در بر گیرد. چرا که هر زیر کلاسی از Vehicle به آن نیاز ندارد اما زیر کلاس Car به آن نیاز دارد.
خط 10: با تعریف یک Constructor بدون ورودی، Constructor پیش فرض کلاس Car را تعریف می کنیم تا عمل خاصی ( که اختصاص یک Type صحیح است ) را انجام دهیم. فیلد mType در کلاس Vehicle تعریف شده است اما چون در Car به ارث رسیده قابل استفاده است. با اختصاص ثابت موجود در Vehicle تعریف می کنیم که Type این کلاس TYPE_CAR است. بجای Type از هر عبارت دیگری می توان استفاده کرد و Type یک کلمه کلیدی خاص نیست. همینطور دقت داشته باشید که با اجرای Constructor در زیر کلاس ها Constructor ساده ( بدون ورودی ) در کلاسهای super هم اجرا خواهد شد. برای تست اجرا شدن Constructor ها و بررسی متد ها می توانید با بررسی Log ها قضیه را بهتر متوجه شوید.
کلاس Formula:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03
04 public class Formula extends Car ..
05
06 }
از کلاس Car میراث بری کرده است پس علاوه بر میراث برای از Vehicle که superclass کلاس Car است ، Member های کلاس Car یعنی mWheelCount را هم به ارث خواهد برد. از طرف دیگر هیچ Constructor ـی برای آن تعریف نشده و همچنان Constructor پیش فرض آن عمل می کند. از آنجاییکه Constructor اصلی کلاس Car هم باید اجرا شود پس بطور اتوماتیک تایپ Formula هم TYPE_CAR خواهد شد. سپس Constructor اصلی Vehicle اجرا خواهد شد و یک عدد به Vehicle ها اضافه می شود.
کلاس Truck:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 public class Truck extends Car ..
04
05 public Truck() ..
06 mWheelsCount = 18;
07 }
08 }
کلاس Truck هم زیر کلاسی از Car است و Constructor اصلی آن تعریف شده است. پس به علاوه انجام عمل در این Constructor یعنی Set کردن تعداد چرخها با عدد 18 به Constructor اصلی Car و سپس Vehicle می رود و باقی آن نتیجه ای مانند Formula در پس خواهد داشت.
کلاس Boat:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 import android.util.Log;
04
05 public class Boat extends Vehicle ..
06
07 public Boat() ..
08 mType = Vehicle.TYPE_BOAT;
09 }
10
11
12
13 @Override
14 public void startMove() ..
15 Log.i("LOG", "Boat Moving");
16 super.startMove();
17 }
18
19
20
21 @Override
22 public void turnOn() ..
23 Log.i("LOG", "No Need to Turn On");
24 }
25
26
27
28 @Override
29 public void turnOff() ..
30 Log.i("LOG", "No Need to Turn Off");
31 }
32
33
34
35 @Override
36 public int getType() ..
37 Log.i("LOG", "Another Pass for getType");
38 return super.getType();
39 }
40 }
خط 8: که داخل Constructor اصلی قرار دارد باعث Set شدن مقدار mType با TYPE_BOAT می شود.
خط 13 تا خط 39: متدهای موجود در Vehicle اصطلاحاً Override خواهند شد. با Override کردن یک متد، آن متد دیگر اجرا نمی شود. به طور مثال اگر متد startMove در این کلاس تعریف نشده بود به علت میراث برای، متد startMove داخل Vehicle اجرا می شود اما با Override کردن آن دیگر متد داخل Vehicle اجرا نخواهد شد. چنانچه تمایل داشتیم علاوه بر کارهای جدید موجود در این متد، متد موجود در کلاس superclass کلاس Boat هم اجرا شود می توانیم از super استفاده کنیم. با این کار دستور می دهیم که متد startMove کلاس Vehicle که super کلاس Boat هست هم اجرا شود. همینطور در خط 38 هم دوباره درخواست اجرای متد درون Vehicle را داده ایم با این تفاوت که این متد خروجی هم دارد پس اگر تمایل داشته باشیم می توانیم مانند کدی که نوشته شده خروجی superclass را به عنوان خروجی این متد در نظر بگیریم.
در خطوط 22 و 29: چون متدها Override شده اند و از super برای فراخوانی متدهای داخل superclass استفاده نشده است پس دیگر متدهای همسان superclass اجرا نخواهند شد.
کلاس Airplane:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 public class Airplane extends Vehicle ..
04
05 public final static int FLYTYPE_NA = 0;
06 public final static int FLYTYPE_MOTOR = 1;
07 public final static int FLYTYPE_MECHANICAL = 2;
08
09 protected int mFlyType = Airplane.FLYTYPE_NA;
10
11
12
13 public Airplane() ..
14 mType = Vehicle.TYPE_AIRPLANE;
15 }
16 }
خط 5 تا 7: علاوه بر میراث بری از کلاس Vehicle تعدادی ثابت هم در این کلاس به صورت public تعریف شده اند بنابراین با استفاده از Airplane.FLYTYPE قابل استفاده خواهند بود.
خط 9: یک فیلد protected با مقدار پیش فرض 0 تعریف شده چرا که FLYTYPE_NA مقدار 0 را در بر دارد.
خط 13: Constructor اصلی تعریف شده و مقدار Type در آن Set می شود.
کلاس Glider:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 public class Glider extends Airplane ..
04
05 public Glider() ..
06 mFlyType = Airplane.FLYTYPE_MECHANICAL;
07 }
08 }
یک Airplane است ( از کلاس Airplane میراث بری کرده است ، زیر کلاس Airplane است ) پس خواص آن مثلا mFlyType تعریف شده که در خط 6 با استفاده از ثابت های تعریف شده در Airplane مقدار دهی می شود.
کلاس Jet:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 public class Jet extends Airplane ..
04
05 public Jet() ..
06 mFlyType = Airplane.FLYTYPE_MOTOR;
07 }
08 }
مانند کلاس Glider با تفاوت اینکه نوع mFlyType آن چیز دیگری متفاوت از Glider تعریف شده است.
حالا طریقه استفاده و ساخت نمونه ای از این کلاسها در چند خط به نمایش در آمده که مفهوم ساده ای در بر دارد. بنابراین آنرا در کلاس AndroidCourseActivity با سورس زیر می توانیم تست کنیم:
کد:
01 package com.uncocoder.course.lessons;
02
03 import android.app.Activity;
04 import android.os.Bundle;
05 import android.view.View;
06 import android.view.View.OnClickListener;
07 import android.widget.Button;
08 import android.widget.TextView;
09 import com.uncocoder.course.lessons.sample1.Car;
10 import com.uncocoder.course.lessons.sample1.Formula;
11 import com.uncocoder.course.lessons.sample1.Glider;
12 import com.uncocoder.course.lessons.sample1.Truck;
13 import com.uncocoder.course.lessons.sample1.Vehicle;
14
15 public class AndroidCourseActivity extends Activity ..
16
17 private Button btn = null;
18 private TextView text = null;
19
20
21
22 /** Called when the activity is first created. */
23 @Override
24 public void onCreate(Bundle savedInstanceState) ..
25 super.onCreate(savedInstanceState);
26 setContentView(R.layout.main);
27
28 initializeUi();
29 }
30
31
32
33 private void initializeUi() ..
34 btn = (Button) findViewById(R.id.btn_action);
35 text = (TextView) findViewById(R.id.txt_caption);
36
37 btn.setOnClickListener(new OnClickListener() ..
38
39 @Override
40 public void onClick(View v) ..
41
42 Vehicle vehicle1 = new Vehicle();
43 Vehicle vehicle2 = new Vehicle(10.0f, 1);
44 Car car = new Car();
45
46 Formula formula1 = new Formula();
47 formula1.turnOff();
48 formula1.turnOn();
49
50 Glider glider = new Glider();
51 Truck truck = new Truck();
52 }
53 });
54 }
55 }
خطوط 42 تا 51: توجه کنید که تعریف شدن یک نمونه Formula هیچ ارتباطی به تعریف شدن Car یا Vehicle ها ندارد بلکه کاملاً مستقل است. همینطور میراث برای برای instance ها وجود دارد و مثلا formula به متدهای تعریف شده در Vehicle مثل turnOn و turnOff دسترسی دارد.
این بود کلیات میراث بری. البته نکات ظریفتری هم وجود دارد که فعلاً دانستن آن برای ما زود است.
Nested Class:
اگر مفهوم Class ها را خوب متوجه شده باشید، Nested Class را هم خوب متوجه خواهید شد. این کلاسهای چیزی نیستند جز اینکه در کلاس دیگری تعریف می شوند و دسترسی به آنها با صدا کردن دو کلاس یا چند کلاس پشت سر هم امکان پذیر است. به مثال زیر که تکمیل شده کلاس Airplane است توجه کنید:
کد:
01 package com.uncocoder.course.lessons.sample1;
02
03 import android.util.Log;
04
05 public class Airplane extends Vehicle ..
06
07 public final static int FLYTYPE_NA = 0;
08 public final static int FLYTYPE_MOTOR = 1;
09 public final static int FLYTYPE_MECHANICAL = 2;
10
11 protected int mFlyType = Airplane.FLYTYPE_NA;
12
13
14
15 public Airplane() ..
16 mType = Vehicle.TYPE_AIRPLANE;
17 }
18
19 public static class Engine ..
20
21 public int mRpm = 10000;
22
23
24
25 public Engine() ..
26 Log.i("LOG", "Engine Created");
27 }
28 }
29 }
خط 20: یک Nested Class جدید داخل Airplane تعریف می شود. به این دلیل static تعریف شده که با استفاده از Airplane.Engine بتوان به آن دسترسی داشت. به دلیل اینکه از چیزی میراث برای نکرده است پس فقط از کلاس Object میراث بری کرده است. توجه داشته باشید که وجود آن در کلاس Airplane به معنی میراث بری از Airplane نیست بلکه اصطلاحاً در آن Namespace تعریف شده است.
خط 22: مانند هر کلاس دیگری می تواند ویژگیهای خاص خود را دارا باشد پس یک فیلد برای نمونه با آن اضافه کردیم.
خط 26: مانند هر کلاس دیگری که می تواند دارای Constructor باشد آنهم Constructor دارد.
پس تفاوتی بین ماهیت Class, Nested Class نیست بلکه نوع دسترسی به آن و مکان تعریف شدن آن، این نام را برای آن برگزیده است.
برای ساخت یک نمونه Nested Class مانند زیر عمل می کنیم.
کد:
01 Airplane.Engine engine = new Airplane.Engine();
سورس برنامه:
می توانید سورس را از آدرس زیر دانلود کنید:
توصیه ها:
- آنقدر با شیء گرایی دست و پنجه نرم کنید که به آن مسلط شوید تا به شما در آینده کمک های زیادی کند.
- شما دیگر نباید در نوشتن الگوریتم های معمولی مشکلی داشته باشید و اگر هنوز مشکلی در این زمینه وجود دارد انواع مثالهای دانشجویی را از اینترنت مطالعه کنید و با فکر خودتان و سرعت عمل زیادتان در تایپ و برنامه نویسی، آنها را حل کنید.