إرسال رسالة نصية عبر الراسبيري باي

مضت فترة طويلة لم أتكلم فيها عن الراسبيري باي ولابد أن نقوم من كل حين وآخر بالحديث عنه ، وفي هذه المرة سيكون الحديث عن إرسال الرسائل النصية عن طريق الراسبيريباي ، وكل الأجهزة المطلوبة هي :

  ١. راسبيري (اي موديل)

  ٢. مودم إتصال (إستخدمت Huawei e1550 ويمكنك إستخدام أي واحد آخر )

لإعداد الراسبيري باي حتى يقوم بإرسال الرسائل نقوم بعمل الآتي :

نقوم بالتأكد من الراسبيري إستطاع التعرُف على المودم وذلك بتوصيل المودم ثم نقوم بفتح ال terminal ونقوم بتنفيذ الأمر

 lsusb

هذا الأمر يقوم بعرض كل الأجهزة المتصلة بالراسبيري عن طريق ال USB  كما واضح بالصورة

 

نقوم بعد ذلك بتثبيت حزمة gammu الذي سنستخدمه لإرسال الرسائل، بتنفيذ الأمر :

 
sudo apt-get install gamma

كما موضح بالصورة

RPI_SMS_gammu

الحزمة تم تثبيتها عندي من قبل لذلك لم يقم بتثبيتها مرة أخرى

بعد ذلك نقوم بمعرفة البورت port المتصل به المودم مع الراسبيري بتنفيذ الأمر :

dmesg | grep ttyUSB

والمودم عندي متصل بالبورت ttyUSB0

ثم نقوم بتحديث إعدادات ال gammu  بالبورت الخاص بالمودم حتى يستخدمه للإرسال وذلك بتنفيذ الأمر :

 sudo gamma-config

وعند تنفيذه يعرض الآتي :

RPI_SMS_gammu-config

نقوم بإختيار port  ونضغط enter فيظهر لك الخيار التالي :

RPI_SMS_gammu-config2

تقوم بتغيير /dev/mobile إلى /dev/ttyUSB0  أو بحسب البورت الذي ظهر عندك عند تنفيذ الأمر

dmesg | grep ttyUSB

فيصير كالتالي :

RPI_SMS_gammu-config3

تضغط enter لترجع للشاشة الأولى ثم تختار save وتضغط enter

فتظهر لك رسالة تفيد أنه تم حفظ الإعدادات

RPI_SMS_gammu-config4

وبذلك نكون قد أتممنا إعداد ال gammu

والخطوة التالية هي تجربة إرسال رسالة نصية وذلك بتنفيذ الأمر :

echo “Hello SMS from Raspberry” | gamma sendsms TEXT 0123456789

وبعد إكتمال الإرسال تظهر كما في الصورة

RPI_SMS_gammu-config5

Advertisements

عودة الحساب

توقفت فترة طويلة عن التدوين لسببين : السبب الأول هو إنشغالي بالعمل الخاص حيث قمت بالإستقالة من الوظيفة والإنتقال إلى العمل  الخاص وقمت بإنشاء شركة مع بعض أصدقائي منذ بداية هذا العام وكنت أود الكتابة حول هذا الموضوع وماهي الفوائد التي إستفدتها في هذه الفترة القصيرة جداً ولكن آثرت تأجيلها إلى بعد إكمال عام كامل على ترك الوظيفة لكي تكون لدي رؤية شاملة (بعض الشيء ) وحتى أكون قد كونت خبرة لا بأس بها وستكون مقالة سنوية بإذن الله اوضح فيها ما استفدته خلال هذا العام من العمل الخاص

وأما الأمر الثاني وهو السبب الرئيس هو أنني فقدت خاصية التحقق بعاملين التي تستخدم لتأمين الحساب في حالة سرقة كلمة المرور  (two factor authentication code)وكنت قد بدأت  مقالاً صغيراً جداً حول هذا الموضوع تجده هنا ، وعندما حاولت ارسال الكود

إلى رقم هاتفي المسجل في وورد برس لم تصل الرسالة ابداً فقمت بمراسلة الدعم الفني لوورد برس وكان تعاملهم معي راقياً جداً حيث قاموا بإرسال كودين في الإيميل إستخدمت أحدهما للدخول والآخر لتعطيل التحقق بعاملين ثم تفعيله وتحديثه بواحد آخر

وحتي يكون الأمر واضحاً دعني أوضح لك مدى فائدة التحقق بعاملين في تأمين الحساب ، حيث انه في السابق كان التحقق من المستخدم يتطلب منه فقط حفظ كلمة المرور وهذا يسمى بعامل المعرفة وهو أكثر عوامل التحقق إستخداماً ومثاله كلمة المرور وارقام ال PIN codes  مثل التي تستخدم في الصراف الآلي .

والعامل الثاني من عوامل التحقق من المستخدم هو عامل الملكية أي يتم إستخدام شيء يمتلكه المستخدم ليتم التحقق منه مثل هاتفه أو بطاقة الصراف الآلي وأجهزة التوكن (Tokens) وغيرها مما يكون مملوكاً للمستخدم .

وأما العامل الثالث فهو العامل الوراثي وهو إستخدام شيء من المستخدم نفسه مثل بصمة الإصبع أو قزحية العين وغيرها.

ويكون النظام آمناً إذا قام بإستخدام أكثر من عامل للتحقق من المستخدم وهذا ما تطبقه كثير من المواقع مثل وورد برس وجي ميل وغيرها حيث غالباً مايتم إستخدام عامل المعرفة بكلمة مرور مع عامل الملكية حيث تقوم بإستخدام تطبيق في هاتفك يقوم بتوليد رمز دخول بحسب الزمن أو بطلب من المستخدم بضغط زر لتوليد الكود  ومن أشهر التطبيقات التي تقوم بهذا العمل : 

lastpass authenticator  و google authenticator

والسؤال هل إستخدام هذه التطبيقات آمن وهل يحتاج لإتصال بالإنترنت لتوليد الكود ؟ الجواب ان استخدامها آمن ولا يحتاج للإتصال بالإنترنت إذا كيف تحدث هذه العملية ؟ ببساطة يقوم المخدم (الذي تريد تأمين حسابك فيه مثلاً وورد برس) بتوليد رمز سري ويظهره لك في شكل QR code لتقوم بإستخدام تطبيق الهاتف بعمل scan وحفظ الرمز السري. 

إذا في المخدم يوجد رمز سري مرتبط بحسابك  ويقوم المخدم بتوليد رمز الدخول (يسمي OTP : One Time Password) مستخدماً الرمز السري والزمن الحالي بإستخدام خوارزمية تدعى TOTP

وفي التطبيق يوجد نفس الرمز (الذي قمت بمسحه عن طريق ال QR code) والزمن ويقوم التطبيق بتوليد رمز جديد كل ١٥ ثانية وعند دخولك للمخدم يطلب منك المخدم الرمز فتقوم بإدخال الرمز الظاهر في التطبيق فيقوم المخدم بحساب نفس الرمز عن طريق الرمز السري المشترك بين المخدم والتطبيق والزمن ويقارنه بما أدخلته فإن تساوت تتم عملية التحقق بنجاح وإلا يرفض المخدم عملية الدخول

 ولتفعليها في الوورد برس بعد الدخول قم بالذهاب إلى خيارات الأمان أو من هذا الرابط https://wordpress.com/me/security/two-step

ثم قم بتفعيل التحقق ولا تنسى تحميل تطبيق google authenticator   تجده للاندرويد من هذا الرابط

https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en

وللآيفون من هنا 

https://itunes.apple.com/en/app/google-authenticator/id388497605?mt=8

في وورد برس بعد تفعيلك للتحقق المتعدد يقوم بإعطائك ١٠ أكواد إحتياطية تستخدمها في حالة فقدان هاتفك وكل كود من هذه العشرة يستخدم لمرة واحدة فقط 

ولي تجربة مع التحقق بعاملين في الفيس بووك حيث كنت استخدم بريد إلكتروني أنشأته في الجامعة وكانت كلمة المرور المستخدمة فيه ضعيفة جداً وتمت سرقة الإيميل ولم أهتم بالأمر كثيراً لأني كنت قد إنتقلت إلى الجي ميل وبعد عدة سنوات تفاجأت أن حسابي في الفيسبوك تم تغيير كلمة المرور الخاصة به ولكن لم يستطع اللص الدخول إلى حسابي لأني كنت قد فعلت التحقق بعاملين فقمت بإرجاع الحساب وتغيير كلمة المرور والبريد والحمد لله وفي هذه المقالة تجد الخطوات اللازمة لتأمين حسابك في الفيسبوك 

Builder نمط ال

هو أحد أنماط الإنشاء (Creational design pattern) ويشبه كثيراً النمطين السابقين (Factory & Abstract factory) الذي تحدثنا عنهما في هذه المقالة والثانية هنا. ونمط ال Builder تم وضعه لحل مشاكل ظهرت في النمطين السابقين وهي في حالة إردنا إنشاء object بعدد كثير من الخصائص مثلاً في مثالنا السابق في ال Abstract factory كان ال superclass يحتوي على خصائص قليلة وهي الرام RAM والقرص الصلب HDD والمعالج CPU ولكن إن زاد عدد الخصائص فليس من المناسب العمل بنمط ال factory أو ال Abstract factory بل المناسب هنا هو نمط ال Builder الذي نحن بصدد الحديث عنه الآن .

هناك العديد من المشاكل عند إستخدام نمط ال Factory أو ال Abstract factory بعدد كثير من الخصائص وهي :

سوف تقوم بتمرير عدد كبير من القيم arguments عند إستخدامك لكلاس ال Factory والذي يمكن أن يؤدي إلى أخطاء عديدة

بعض القيم parameters قد تكون إختيارية ولا نحتاج إليها لحظة إنشاء ال object ولو إستخدمنا نمط ال Factory أو Abstract factory فسوف نضطر إلى تمرير null في هذه الحالة.

يمكننا حلُّ هذه المشاكل بتمرير جميع المدخلات المطلوبة لدالة الإنشاء (constructor) مع مجموعة من دوال ال setter & getter  لإعطاء المتغيرات الإختيارية قيم إبتدائية لكن المشكلة في هذه الطريقة هي أنه يجب إعطاء جميع المتغيرات المطلوبة قيماً وهذا ما يمكننا حله بكل بساطة بإستخدام نمط ال Builder الذي يُستخدم عادة عند وجود عدد كبير من المتغيرات الإختيارية ويقوم ببناء ال object خطوة خطوة إلي حين يكتمل الإنشاء .

أولاً نقوم بإنشاء كلاس داخلي static inner class ونقوم بنسخ كل المتغيرات من الكلاس الأساسي outer class للكلاس الداخلي (وهذه هي مشكلة هذا النمط أن فيه تكرار ) الذي عادة ما يسمى بإسم ينتهي بكلمة Builder (يمكنك أن تسميه بما تشاء ) مثلاً إذا كان الكلاس إسمه printer فيكون اسم الكلاس الداخلي PrinterBuilder وهكذا .

كلاس ال Builder (الكلاس الداخلي) يجب أن يحتوى على دالة إنشاء (constructor) تقوم بإستقبال جميع المتغيرات المطلوبة (required attributes)

كلاس ال Builder يجب أن يحوى دوال تعيين للمتغيرات الإختيارية (setter for optional attributes) ويجب أن تقوم هذه الدوال بإرجاع كائن من نفس نوع ال Builder كلاس

الخطوة الأخيرة هي إنشاء دالة (في الغالب تُسمى ()build) في كلاس ال Builder تقوم بإرجاع كائن من الكلاس المطلوب فلو مثلاً قمنا بإنشاء كلاس Printer وقمنا بعمل كلاس Builder له بإسم PrinterBuidler فإن دالة build تقوم بإرجاع كائن (object) من نوع Printer وتكون دالة الإنشاء (constructor) في الكلاس الأساسي (Printer) تكون private حتى نمنع إنشاء ال object منها بطريقة مباشرة بل يتم الإنشاء عن طريق كلاس ال Builder ويجب أن يستقبل هذا ال constructor كائن من نوع ال Builder

وستتضح كل هذه الأمور بالمثال إن شاء الله .

مثال : نريد إنشاء كلاس يقوم بعرض Dialog للمستخدم وسنقوم بإستخدام نمط ال Builder كالآتي :

public class Dialog {

//required parameters

private String title ;

private int type ;

//optional parameters

private String message ;

private boolean closable ;

private int icon ;

public String getTitle () {

return this.title ;

}

public int getType() {

return this.type;

}

public String getMessage(){

return this.message ;
}

public boolean isClosable() {

return this.closable ;

}

public int getIcon(){

return this.icon ;

}

private Dialog(DialogBuilder builder){

this.title = builder.title ;

this.type = builder.type ;

this.message = builder.message ;

this.closable = builder.closable;

this.icon = builder.icon;

}

public void show(){

//logic to show the dialog ….

}

public static class DialogBuilder {

//required parameters

private String title ;

private int type ;

//optional parameters

private String message ;

private boolean closable ;

private int icon ;

public DialogBuilder(String title , int type){

this.title = title ;

this.type = type ;

}

public DialogBuilder setMessage(String message){

this.message = message ;

return this;

}

public DialogBuilder setClosable(boolean closable){

this.closable = closable ;

return this;

}

public DialogBuilder setIcon(int icon){

this.icon = icon ;

return this ;

}

public Dialog build() {

return new Dialog(this);

}

}
}

ولإختباره

public class Test {

public static void main(String [] args){

Dialog dialog = new Dialog.DialogBuilder(“test”,DIALOG_ALERT)

.setMessage(“Hello world “)

.setClosable(true)

.setIcon(DIALOG_INFO_ICON);

dialog.show();

}

}

أمثلة لنمط ال Builder

StringBuilder

(Lambda expressions) تعبيرات اللامدا

منذ بداية لغة الجافا ١٩٩٦ ويواصل مطوروا اللغة تطويرها وزيادة العديد من الخصائص المهمة فيها ولكن هناك تغييرات أدت إلى نقلة كبيرة في اساسيات اللغة نفسها حتى إنها غيرت طريقة كتابة أكواد اللغة نفسها! وأكبر تغيير وتطوير تم في لغة جافا منذ إنشائها إلى الآن هو إضافة ال Generics وكان ذلك في العام ٢٠٠٤ عند إطلاق جافا ٥ والمسمى ب “Tiger” والإضافة الكبيرة الثانية كانت في العام ٢٠١٤ عند إطلاق جافا ٨ وكانت هي إضافة تعبيرات لامدا (Lambda expressions) والتي نحن بصدد الكلام عنها اليوم ،

ولكي نفهم تعبيرات لامدا لابد لنا من معرفة ال functional interface وهي interface عادية لكن فيها دالة واحدة فقط مثل ال Runnable interface الذي يستخدم في ال Threading في الجافا ،

وتعبير اللامدا (lambda expression) هي دالة مجهولة (anonymous or unnammed) ، وهذه الدالة لايتم تنفيذها هي نفسها ولكنها تُستخدم لتطبيق (implement) الدالة المُعرفة في ال functional interface وسيتضح هذا التعريف مع الأمثلة إن شاء الله.

تعبيرات لامدا أضافت تركيبات (syntax) ومعاملات (operator) جديدة للغة الجافا ، المُعامل الجديد يُسمى lambda operator وهو


->

وتُقرأ  (becomes) أو (goes to )

وهذا المُعامِل يقوم بتقسيم تعبير اللامدا إلى جزئين : الجزء الذي على اليسار يتم في تحديد المُدخلات (parameters) المطلوبة لتعبير اللامدا وأما الجزء على يمين المُعامِل هو جسم تعبير اللامدا نفسه (lambda experssion body) .

لنعط مثالاً على ما سبق :

اولا نقوم بتعريف ال functional interface


interface MyFunctional {

        double getValue();

}

 ثم نقوم بتعريف تعبير اللامدا


MyFunctional fun ;

fun = () ->  43.2;

لأن الدالة الموجودة في ال functional interface لا تستقبل أي متغيرات لم نقم بتمرير اي قيم لمدخلات اللامدا واستخدمنا اقواس خالية () وفي الجانب الأيمن من مُعامِل لامدا أرجعنا القيمة 43.2  لأن الدالة تقوم بإرجاع متغير من نوع double مع ملاحظة أننا لم نقم بكتابة return لأن جسم اللامدا () لاتوجد فيه إلا عبارة واحدة (وهذا النوع يُسمى single expression lambda) وأما إن كان فيه أكثر من أمر (وهذا النوع يُسمى block lambda expression) فحينئذ نستخدم أقواس البداية والنهاية ونجعل return في النهاية كما في هذا المثال :


fun = () -> {

   double t = 100;

   double r = Math.random() * t ;

   System.out.println("lambda expression body");

   return r ;

}

double  value = fun.getValue();

System.out.println("value = "+value);

مثال آخر لتعبير لامدا يستقبل متغير


interface LambdaInterface {

boolean test(int n);

}

ونقوم بإستخدامه كالآتي :


LambdaInterface isEven = (n) -> (n%2) == 0;

if(isEven.test(10)) System.out.println("is even number");

if(!isEven.test(9)) System.out.println("is not even");

LambdaInterface isNonNeg = (n) -> n >=0 ;

if(isNonNeg.test(1)) System.out.println("not negative");

if(!isNonNeg.test(-1)) System.out.println("negative");

بدون هذه تعبيرات اللامدا كنا نقوم بإنشاء ما يُسمى بالكلاس المجهول (anonymous class) وأما الآن فلا حاجة للكلاس المجهول، وهذا مثال لإنشاء Thread بدون اللامدا ومرة بإستخدامه

بدون اللامدا :


Runnable myRunnable = new Runnable(){

public void run(){

System.out.println("Runnable running");

}

}

Thread thread = new Thread(myRunnable);

thread.start();

واما بإستخدام اللامدا


Thread thread = new Thread(

()->{ System.out.println("Runnable running");}  );

thread.start();

Abstract Factory نمط

هو أحد أنماط الإنشاء ويشبه نمط ال Factory كثيراً مع إختلاف هو أننا يمكننا أن نشببه بال factory of factories .

في المقال السابقة التي تحدثت فيها عن نمط ال Factory قمنا بعمل كلاس Factory واحد هو المسئول عن إنشاء ال sub-class المناسب بحسب مدخل معين لدالة الإنشاء وإستخدمنا if-else لإنشاء ال sub-class وكان يمكننا إستخدام switch أيضاً لفعل نفس الأمر.

أما في نمط Abstract factory هـذا فلا نحتاج لإستخدام if-else أو switch بل سنقوم بعمل كلاس factory لكل sub-class وبعدها نقوم بإنشاء كلاس Abstract factory يقوم بإنشاء ال sub-class بحسب مُدخل من نوع كلاس ال factory ، في البداية يبدو الأمر معقداً لكن مع التطبيق العملي ستبدو ساهلة وواضحة للغاية ، سنقوم بالتعديل على نفس المثال السابق كما يلي :

public abstract class Computer {

   public abstract  String getRAM();

   public abstract String getHDD();

   public abstract String getCPU();

   @Override

   public String toString(){

     return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+
            ", CPU="+this.getCPU();
 
  }

}
public class PC extends Computer {

 private  String ram; 
 private  String hdd; 
 private  String cpu;

 public PC(String ram, String hdd, String cpu){ 
    this.ram=ram;
    this.hdd=hdd;
    this.cpu=cpu; 
 }

 @Override
 public String getRAM() { 
    return this.ram;
 }

 @Override
 public String getHDD() {
    return this.hdd;
 }

 @Override
 public String getCPU() { 
   return this.cpu;
 } 
}
public class Server extends Computer {

   private String ram; 
   private String hdd; 
   private </b>String cpu;

   public Server(String ram, String hdd, String cpu){ 
     this.ram=ram;
     this.hdd=hdd;
     this.cpu=cpu; 
   }

   @Override
   public  String  getRAM() {
     return this.ram;
   }

  @Override
  public String getHDD() {
   return this.hdd;

 }
 @Override
 public String getCPU() {
  return this.cpu;
 } 
}

الخطوة التالية هي إنشاء كلاس factory لكل sub-class

وقبلها نقوم بإنشاء كلاس ال Abstract Factory ويمكننا أن نقوم بتعريفه ك abstract class أو interface

public interface ComputerAbstractFactory {

  public Computer createComputer(); 
}

مع ملاحظة ان الدالة createComputer تقوم بإرجاع كائن (object) من نوع الكلاس الأب Computer

وسنقوم بإنشاء  كلاسات ال factory التي تقوم بتطبيق هذا ال interface

public class PCFactory implements ComputerAbstractFactory {

 private String ram;
 private String hdd;
 private String cpu;

 public PCFactory(String ram, String hdd, String cpu){ 
   this.ram=ram;
   this.hdd=hdd;
   this.cpu=cpu; 
 }

 @Override
 public Computer createComputer() {
   return new PC</b>(ram,hdd,cpu);
 }
}

وأيضاً كلاس ال ServerFactory

public class ServerFactory implements ComputerAbstractFactory {

 private String ram;

 private String hdd;

 private String cpu;

 public  ServerFactory (String ram, String hdd, String cpu){ 
    this.ram=ram;
    this.hdd=hdd;
    this.cpu=cpu; 
 }

 @Override
 public Computer createComputer() { 
   return new Server(ram,hdd,cpu);
 } 
}

والآن سنقوم بإنشاء الكلاس الذي يوفر لنا طريقة إنشاء الكائنات (Objects) المناسبة لكل كلاس مما سبق

public class ComputerFactory {

  public static Computer getComputer(ComputerAbstractFactory factory){

     return factory.createComputer(); 
  }

}

والان تبقى لنا إنشاء تطبيق لتجربة الإنشاء عليه :

public class TestDesignPatterns {

  public static void main(String[] args) { 
    testAbstractFactory();
  }

  private static void  testAbstractFactory() { 

    Computer pc =  ComputerFactory.getComputer(
                       new PCFactory("2 GB","500 GB","2.4 GHz"));

    Computer server = ComputerFactory.getComputer(
                       new ServerFactory("16 GB","1 TB","2.9 GHz"));

    System.out.println("AbstractFactory PC Config::"+pc);
    System.out.println("AbstractFactory Server Config::"+server);

  }
}

فوائد إستخدام ال Abstract Factory Pattern:

يوفر لنا خاصية مهمة جداً في تصميم البرمجيات وهي “Code for interface rather than implementation” الذي يجعل الكود أكثر قابلية للتعديل والزيادة عليه ، لاحظ أننا قمنا بتمرير كائن (Object) من نوع ComputerAbstractFactory للدالة getComputer في الكلاس ComputerFactory وهذا مفيد جداً في حالة أردت إضافة كلاس جديد كل ما عليك فعله هو أن تقوم بإنشاء كلاس يرث من ComputerAbstractFactory interface وتقوم بعمل factory class له. جرب أن تقوم بإضافة كلاس Laptop للكود السابق.

أمثلة لنمط ال Abstract factory في مكتبة الجافا الأساسية JDK :

java.xml.parsers.DocumentBuilderFactory

java.xml.transform.TransformerFactory

javax.xml.xpath.XPathFactory

Factory نمط ال

هو أحد أنماط الإنشاء (creational design pattern)  ويعد من الأنماط المستخدمة بكثرة في مكتبات الجافا مثل ال Spring و ال Struts وغيرها .

متى نستخدم نمط factory :

يتم إستخدامه عندما يكون لدينا super class ولديه عدد من ال sub-classes ونريد إنشاء object من واحد منها بناء على مدخلات معينة .

في البدء دعونا نقوم بمعرفة كيفية تطبيق نمط ال factory في الجافا ثم بعد ذلك نتعرف على فوائده وإستخداماته في مكتبات الجافا الأساسية JDK

ال Super Class :

ال super class في نمط ال factory إما أن يكون interface أو يكون abstract class غالباً ولا مانع أن يكون class جافا عادي ، ميزة ال interface أنه يجعلك تبني قالباً تجبر من خلاله كل ال sub-classes التي تقوم بعمل وراثة منه بتطبيق الدوال الموجودة في ال super-class ولكن كل واحد من ال sub-class تقوم بالتطبيق بطريقة مختلفة من الأخرى لكن هيكل الدالة (مدخلات الدالة ومخرجها) واحد فيها كلها وأما ال abstract class ففيه نفس ميزات ال interface ولكنك تستطيع فيه بناء دالة (method) كاملة ولكن لأن الجافا لا تدعم الوراثة المتعددة فلا يمكن أن ترث من أكثر من super-class لذا فعند إستخدام ال interface يمكنك وراثة أكثر من واحد وأما في حالة إستخدامك ال abstract class فلن تستطيع أن ترث إلا واحدة فقط.

مثال ل super class وسنقوم بإستخدام abstract class في هذا المثال :

public abstract class Computer {

public abstract String getRAM();

public abstract String getHDD();

public abstract String getCPU();

@Override

public String toString() {

return “RAM= ”+this.getRAM()+”, HDD=“+this.getHDD()+”, CPU=“+this.getCPU();

}

}

ال Sub classes :

دعونا نفترض أن لدينا sub class إسمه PC وآخر إسمه Server وكلهم يرثوا من Computer الذي قمنا ببنائه كما سبق


public class PC extends Computer {

private String ram;

private String hdd;

private String cpu;

public PC(String ram,String hdd,String cpu){

this.ram = ram;

this.hdd = hdd;

this.cpu = cpu;

}

@Override

public String getRAM(){

return this.ram;

}

@Override

public String getHDD(){

return this.hdd;

}

@Override

public String getCPU(){

return this.cpu;

}

}

وكذلك ال Server


public class Server extends Computer {

private String ram;

private String hdd;

private String cpu;

public Server(String ram,String hdd,String cpu){

this.ram = ram;

this.hdd = hdd;

this.cpu = cpu;

}

@Override

public String getRAM(){

return this.ram;

}

@Override

public String getHDD(){

return this.hdd;

}

@Override

public String getCPU(){

return this.cpu;

}

}

ال Factory class :

الآن لدينا ال super class جاهز وكذلك ال sub classes  وكل ما تبقى هو ال factory class الذي سنقوم من خلاله بإنشاء object من ال classes السابقة


import Computer;

import PC;

import Server;

public class ComputerFactory {

public static Computer getComputer(String type,String ram,String hdd,String cpu){

if(type.equalsIgnoreCase(“PC”))

return new PC(ram,hdd,cpu);

else if (type.equalsIgnoreCase(“Server”))

return new Server(ram,hdd,cpu);

return null;

}
}

مع ملاحظة :

يمكنك بناء ال factory class بإستخدام نمط ال singleton الذي تكلمنا عليه في المقالة السابقة أو يمكنك جعل الدالة التي تقوم بإنشاء ال subclass من النوع static حتى لا تقوم بإنشاء object من كلاس ال factory

بحسب قيمة معينة يتم تمريرها لدالة الإنشاء في ال factory class تقوم بإنشاء الكلاس المناسب

وهـذا مثال لكيفية إستخدام ال factory class السابق :


public class TestFactory {

public static void main(String [] args){

Computer pc = ComputerFactory.getComputer(“pc”,”2 GB”,”500 GB”,”2.4 GHz”);

Computer server = ComputerFactory.getComputer(“server”,”16 GB”,”1 TB”,”2.9 GHz”);

 System.out.println(“Factory PC Config::”+pc);

 System.out.println(“Factory Server Config::”+server);

 }

}

مخرجات البرنامج السابق :


Factory PC Config::RAM= 2 GB, HDD=500 GB, CPU=2.4 GHz

Factory Server Config::RAM= 16 GB, HDD=1 TB, CPU=2.6 GHz

فوائد إستخدام نمط factory :

  • يقوم بعزل عملية إنشاء ال objects  من التطبيق الرئيسي (main class) وذلك يجعل التعديل سهلاً فمثلاً لو قمنا بتغيير ال PC class من الداخل فلن يؤثر ذلك على TestFactory

  • يقوم بعمل تجريد (abstraction)

إستخداماته في مكتبات الجافا الأساسية JDK :

  • في java.util.Calendar

  • في الدالة valueOf() الموجودة في الكلاس Boolean,Integer,…

أنماط التصميم في الجافا

لغة الجافا من أكثر لغات البرمجة تطوراً وأكبرها إنتشاراً وشهرة, ويمكن القول بإنها تكنولوجيا كاملة لا مجرد لغة برمجة فيمكن إستخدامها في العديد من المنصات وأنظمة التشغيل المختلفة والنظم المدمجة وغيرها ويمكن تخصيص آلة الجافا الإفتراضية (JVM) لتناسب بيئة التشغيل المعينة التي نريد تشغيل جافا عليها مثل ما فعلت شركة قوقل بتخصيص ال JVM لبيئة الهواتف المحمولة في نظامهم الأندرويد وأطلقوا عليها Dalvik virtual machine , إذا فلغة جافا لغة مرنة جداً ويمكن تشغيلها في بيئات مختلفة ,, ولكن ينبغي على مبرمج من مبرمجي الجافا أن يكون على دراية بالطرق المناسبة لكتابة الكود البرمجي حتى لا تحدث المشاكل التي لا تُحمد عقباها.

ما هي أنماط التصميم ؟

نمط التصميم هو أفضل طريقة لحل مشكلة معينة تواجه كل مبرمج يستخدم لغة جافا, ويُتوصَّل إليها بالخبرات والتجارب

ويمكن تقسيم أنماط التصميم إلى ثلاث مجموعات تندرج تحت كل منها عدد من الأنماط :

1. نمط الإنشاء (Creational design pattern) :

هو الذي يهتم بإيجاد حلول لمشاكل إنشاء ال object بأفضل طريقة ممكنة بمعنى أنه ليس من المناسب إنشاء ال object بإستخدام الكلمة المحجوزة new في أي حالة أن يكون ذلك مناسباً فقد تحتاج في بعض الحالات أن تخفى طريقة الإنشاء من كل من يستخدم ال class الذي قمت بإنشاءه , وأيضاً في بعض الحالات ليس من المناسب أن تقوم بإنشاء object من ال class الذي تريد الوصول إلى وظائفه في كل مرة تنشئ object فقد يؤثر ذلك في الأداء. تندرج تحت هذا النمط 5 طرق وهي :

   1. نمط Singleton

   2. نمط Factory

   3. نمطAbstract factory

   4. نمط Builder

   5. نمط Prototype

2. نمط الهيكلة (Structural design pattern ):

هي مجموعة الأنماط التي تهتم بتوفير حلول مختلفة لإنشاء هيكل ال class . وتندرج تحت هذا النمط 7 طرق وهي :

   1. نمط Adapter

   2. نمط Composite

   3. نمط Proxy

   4. نمط Flyweight

   5. نمط Facade

   6. نمط Bridge

   7. نمط Decorator

3. نمط السلوك (Behavioral design pattern):

هي مجموعة الأنماط التي تهتم بتوفير حلول لأفضل طرق العلاقات بين ال objects . وتندرج تحت هذا النمط 11 طريقة وهي :

   1. نمط Template method

   2. نمط Mediator

   3. نمط Chain of responsibility

   4. نمط Observer

   5. نمط Strategy

   6. نمط Command

   7. نمط State

   8. نمط Visitor

   9. نمط Interpreter

   10. نمط Iterator

   11. نمط Memento

وسأقوم بحول الله وقوته بشرح جميع هذه الأنماط في سلسلة متتالية من المقالات ونبدأ بالنوع الأول من الإنماط الإنشائية وهو نمط Singleton. وسأقوم بإتباع طريقة بسيطة في كل نمط وهي تعريف النمط – متى تستخدم – المشاكل التي تستطيع حلها بإستخدام هذا النمط – الطرق التي يتم بها تطبيق هذا النمط – مثال عملى للنمط – أين تم إستخدام النمط في مكتبة جافا الأساسية إن وجدت

نمط Singleton :

تعريفه : هو نمط يستخدم لإنشاء object واحد فقط من ال class يعني في كل مرة تريد الوصول لأي وظيفة من وظائف ال class يتم إستخدام نفس ال object المحجوز في الذاكرة ولا يتم إنشاء واحد جديد. وهذا النمط يمنع إنشاء ال object مباشرة بأن يقوم بعمل private constructor لل class

متى يستخدم ؟ يستخدم في حالات كثيرة منها logs و ال driver object مثل محركات قواعد البيانات وأيضاً في cashing وفي Thread Pool وغيرها كثير

ولكي تقوم بتطبيق هذا النمط يجب عليك مراعاة الآتي :

   • ال constructor يجب أن يكون من نوع private حتى يمنع إنشاء ال object بطريقة مباشرة من خارج ال class بإستخدام الكلمة المحجوزة new

   • متغير من نوع private static من نفس ال class والذي يتم عن طريقه إنشاء ال object من ال class

   • دالة (method ) من نوع public static تقوم بإرجاع ال object من ال class حتى يتم الوصول إليها من أي class آخر

المشاكل التي تم حلها بإستخدام هذا النمط :

الطرق التي يمكن بها تطبيق نمط ال Singleton كثيرة وهي :

1. Eager Initialization :

في هذه الطريقة يتم إنشاء ال object في داخل جسم الكلاس وتقوم ال JVM بتحميله وإنشائه عند تحميل الكلاس بواسطة ال class loader

مثال

public class EagerSingletonExample {
	private static final EagerSingletonExample instance = new EagerSingletonExample();

	private EagerSingletonExample(){}

	public static EagerSingletonExample getInstance() {
		return instance;
        }
}

ولكن هذه الطريقة فيها بعض القصور وهي أنه يتم إنشاء ال object عندما يقوم ال JVM بتحميل الكلاس بواسطة ال class loader وحتى ولو لم يستخدم أحد هذا ال object يكون قد تم حجز مكان له في JVM

2. Static block initialization :

يشبه الطريقة السابقة ولكنه يقوم بإنشاء ال object في static block مما يسهل إضافة خيار معالجة الإستثناءات (exception handling)

public class StaticBlockSingletonExample {
	private static StaticBlockSingletonExample instance;

	private StaticBlockSingletonExample(){}

	static {
		try {
			instance = new StaticBlockSingletonExample();
                }catch(Exception e){
                 }
        }
 public static StaticBlockSingletonExample getInstance(){
	return instance;
 }
}

وأيضاً هذه الطريقة في نفس المشكلة السابقة وهي أنه يتم إنشاء ال object حتى لو لم تقم بمناداة الدالة getInstance وذلك لأن عملية الإنشاء تمت في داخل جسم الكلاس وستقوم ال JVM بتنفيذه عند تحميل الكلاس بواسطة ال class loader

3. Lazy Initialization :

يتم فيها إنشاء ال object داخل الدالة getInstance نفسها وهذه الطريقة تحل المشكلة السابقة

مثال لها

public class LazySingletonExample {
	private static LazySingletonExample instance ;
	private LazySingletonExample(){}
	public static LazySingletonExample getInstance(){
		if(instance == null)
			instance  = new LazySingletonExample();

		return instance ;
        }
}

هذه الطريقة ممتازة ولكن تظهر فيها المشاكل في الأنظمة متعددة العمليات multithreaded systems بالتحديد في حالة وصول أكثر من عملية thread للشرط if بداخل الدالة getInstance لأن ذلك سيؤدي إلى أن كل عملية thread سيكون عندها نسخة object مختلفة مما يخل بنمط ال singleton تماماً
4. Thread safe singleton :
هذه الطريقة تحل المشلكة السابقة في حالة أكثر من عملية thread بإستخدام الكلمة المحجوزة synchronized التي تسمح ل thread واحد فقط أن يقوم بتنفيذ الدالة في نفس الوقت, مثال

public class ThreadSafeSingletonExample {
	private static ThreadSafeSingletonExample instance ;

	private ThreadSafeSingletonExample(){}

	public static synchronized ThreadSafeSingletonExample getInstance(){
	   if (instance == null)
		instance = new ThreadSafeSingletonExample();
	 return instance;
        }
}

هذه الطريقة ممتازة جداً ولكنها تؤدي لتقليل الأداء نسبة لإستخدام ال synchronized ويمكننا معالجة ذلك بجعل ال synchronized داخل الشرط كالاتي :

public static ThreadSafeSingletonExample getInstance(){
  if(instance == null){
    synchronized (ThreadSafeSingletonExample.class){
         if(instance == null){
             instance = new ThreadSafeSingletonExample();
          }
     }
   }
}

5. Bill Pugh singleton :

كل الطرق السابقة تظهر فيها المشاكل في حالة وجود عمليات threads كثيرة جداً تحاول مناداة الدالة getInstance في نفس اللحظة, المشاكل تكون في الذاكرة تحديداً , فجاءت هذه الطريقة بطريقة مختلفة لحل هذه المشكلة بإستخدام inner helper class مثال :

public class BillPughSingletonExample {
   
    private BillPughSingletonExample(){};

    private static class SingletonHelper {
       private static final BillPughSingletonExample INSTANCE = new BillPughSingletonExample();
    }
 public static BillPughSingletonExample getInstance(){
   return SingletonHelper.INSTANCE;
 }
}

مع ملاحظة أن الكلاس الداخلي SingletonHelper لايتم تحميله في ال JVM بواسطة ال class loader عند تحميل الكلاس BillPughSingletonExample بل يتم ذلك عند مناداة الدالة وهذه الطريقة هي أفضل طريقة لتطبيق مفهوم ال singleton لما فيها من حلول لأغلب المشاكل السابقة .
وعموماً يمكنك تطبيق ال singleton بطرق أخرى كثيرة لكن المهم أن لا يكون هناك أكثر من instance للكلاس
أين تم إستخدامه في مكتبة جافا الأساسية : ال singleton مستخدم في كثير من مكتبات الجافا وأقرب مثال له في ال java.lang.Runtime
وسأواصل في بقية الأنماط إن شاء الله تعالى.