Friday, August 18, 2017

ArrayList ও LinkedList মধ্যে পার্থক্য কী? (Difference between ArrayList and LinkedList in Java)

ArrayListLinkedList ক্লাস দুটি লিস্ট ইন্টারফেসকে ইমপ্লিমেন্ট করে। যার ফলে এদের মেথড ও ফলাফল একইরকম হলেও ইম্প্লিমেন্টেশনের দিক থেকে এদের মধ্যে বেশ কিছু পার্থক্য রয়েছে। পার্থক্যগুলো নিয়ে আলোচনা করার আগে লিস্ট ইন্টারফেসের প্রধান মেথডগুলো দেখা যাক-
 
1
2
3
4
5
6
public interface List{
  public E get(int index);
  public void add(E element);
  public void add(E element, int index);
  public void remove(int index);
}

উপরের এই মেথডগুলোর ইমপ্লিমেন্টেশন অ্যারেলিস্ট ও লিংকডলিস্ট দুটো ক্লাসেই রয়েছে। ব্যবহারের দিক থেকে এই মেথডগুলোর কোনো পার্থক্য নেই। উদাহরণ-

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TemplateService {
    public static void main(String[] args) {
        //using LinkedList
        List<String> countries = new ArrayList<>();
        //adding elements to the ArrayList
        countries.add("Bangladesh");
        countries.add("India");
        countries.add("Bhutan");
        countries.add("Pakistan");

        // searching element by index
        String bangladesh = countries.get(0);

        //adding element by index
        countries.add(2, "Indonesia");

        //removing element
        countries.remove(4);

        //using LinkedList
        List<String> cities = new LinkedList<>();
        //adding elements to the LinkedList
        cities.add("Dhaka");
        cities.add("London");
        cities.add("Beijing");

        // searching element by index
        String dhaka = cities.get(0);

        //adding element by index
        cities.add(3, "Islamabad");

        //removing element
        cities.remove(3);
    }
}

ইমপ্লিমেন্টেশনের দিক থেকে ArrayList উপাদানগুলো রাখার জন্য একটি অ্যারে ব্যবহার করে এবং অ্যারের সাইজ প্রয়োজন অনুযায়ী বড় করতে পারে। অন্যদিকে LinkedList ডাবলি-লিকংডলিস্ট ডেটা স্ট্রাকচার ব্যবহার করে ইমপ্লিমেন্ট করা হয়ছে।

এবার উপরের মেথডগুলোর টাইম কমপ্লেক্সিটি দেখা যাক -

মেথডের নাম

অ্যারেলিস্ট

লিংকডলিস্ট

E get(int index)

O(1)

O(n)

void add(E element)

O(1) amortized 

O(1)

void add(E element, int index)

O(n)

O(1)

void remove(int index)

O(n)

O(1) 


উপরের চার্ট থেকে দেখতে পারি যে, অ্যারেলিস্ট থেকে কোনো উপাদান খুব দ্রুত খুঁজে বের করা যায় লিংকলিস্টের চেয়ে। এর কারণ, অ্যারেলিস্ট উপাদানগুলো রাখার জন্য একটি অ্যারে ব্যবহার করে যা থেকে কোনো উপাদানের ইনডেক্স জানা থাকলে কনস্ট্যান্ট টাইমে বের করে আনা যায়। অপর দিকে লিংকলিস্ট যেহেতু ডাবলি-লিংকলিস্ট ডেটা স্ট্রাকচার ব্যবহার করে, তাই কোনো উপাদান খুঁজে বের করতে সবগুলো উপাদান খুঁজতে হয়। এজন্যে লিংকলিস্টে কোনো উপাদান অ্যাকসেস করার সময় সময় O(n)।

অ্যারেলিস্টে ডাইনামিক অ্যারে ব্যবহার করা হয়। আমরা জানি যে অ্যারে একটি নির্দিষ্ট দৈর্ঘ্যের (length) উপাদান রাখতে পারে। তবে ডাইনামিক অ্যারের দৈর্ঘ্য প্রয়োজন মতো বাড়ানো বা কমানো যায়। এটি করার পদ্ধতিটি হলো - প্রথমে একটি নির্দিষ্ট দৈর্ঘ্যের অ্যারে তৈরি করা হয়। অ্যারেটি উপাদান পূর্ণ হয়ে গেলে একটি নতুন দৈর্ঘ্যের অ্যারে তৈরি করা হয় যা আগের অ্যারের দৈর্ঘ্যের চেয়ে বড়। আগের অ্যারে থেকে উপাদানগুলো কপি করে নতুন অ্যারেতে রাখা হয় এবং এতে যে অতিরিক্ত ফাঁকা ঘরগুলো রয়ে যায় সেগুলোতে নতুন উপাদান রাখা হয়। এভাবে আবার অ্যারিটি পুর্ণ হয়ে গেলে নতুন দৈর্ঘ্যের অ্যারে তৈরি করা হয়। তবে নতুন দৈর্ঘ্য একটি চিন্তা ভাবনা করে নেওয়া হয় যাতে প্রতিবার নতুন উপাদান যুক্ত করার জন্য অ্যারের দৈর্ঘ্য বৃদ্ধি করার প্রয়োজন না পারে। যেমন- প্রতিবার অ্যারের দৈর্ঘ্য বৃদ্ধির সময় আমরা আগের দৈর্ঘ্যের চেয়ে দিগুণ নেওয়া পারে। এর ফলে প্রতিবার নতুন উপাদান যুক্ত করার সময় নতুন অ্যারে তৈরি এবং আগের অ্যারে থেকে কপি করার কাজ করতে হয় না, বরং এটি একটি নির্দিষ্ট সময় পর পর প্রয়োজন হয়।

অ্যারেলিস্টের অ্যারেতে প্রথমে একটি 10 দৈর্ঘ্যের অ্যারে থাকে। এটি পূর্ণ হয়ে গেলে নতুন অ্যারে তৈরি করা হয় যা আগের অ্যারে থেকে দেড় গুণ হয়। এই অ্যারের দৈর্ঘ্যের বৃদ্ধির ফরমুলা হলো -

int newCapacity = oldCapacity + (oldCapacity >> 1);

এখানে capacity হচ্ছে অ্যারেলিস্টের ভেতরের অ্যারের দৈর্ঘ্য।


অর্থাৎ আমরা যদি 10 টি উপাদান অ্যারেতে যুক্ত করতে চাই, তাহলে এই অ্যারের দৈর্ঘ্য বৃদ্ধি করার প্রয়োজন নেই। তবে যদি n টি উপাদান যুক্ত করতে চাই, তাহে বেশ কতবার দৈর্ঘ্য বৃদ্ধি করা ও আগের অ্যারে থেকে উপাদান কপি কররে আনার প্রয়োজন পরবে।

তাহলে n সংখ্যক উপাদান যুক্ত করার সময় O(n) + মাঝে মধ্যে কপি অপারেশন (একটি লুপের মাধ্যমে আগের n আইটেম কপি করা)আমরা সাধারণভাবে বলতে পারি, গড়ে এর টাইম কমপ্লেক্সিটি (amortized) O(1)

অ্যারেলিস্টের শেষে যুক্ত না করে মাঝে কেনাে উপাদান যুক্ত করতে হলে যেই উপাদান থেকে পরের উপাদানগুলো শিফট (কপি করে এক ঘর সরানো) করার প্রয়োজন হবে। একদম শুরুতে যদি উপাদান যুক্ত করতে হয় তাহলে সবগুলো উপাদান প্রথমে থেকে ডান দিকে শিফট করতে হবে সেক্ষেত্রে এর কম্প্লিক্সিটি হবে O(n)

অ্যারেলিস্ট থেকে কোনো উপাদান রিমুভ করতে হলেও এই শিফ্টিং বা কপি করার প্রয়োজন পরে। অ্যারের লিস্টের মাঝ থেকে কোনো উপাদান রিমুভ করতে হলে, মাঝ থেকে পরবর্তি উপাদানগুলো কপি করে একঘর সামনে আনতে হয়। উপাদানটি যদি প্রথমে হয়, তাহলে সবগুলো উপাদান কপি করে সামনে আনতে হয়। তবে শেষ থেকে উপাদান রিমুভ করলে এর টাইম কমপ্লেক্সিটি হয় O(1) কারণ এতে কোনো কপি বা শিফটিংয়ের প্রয়োজন হয় না।

সুতরাং দেখা যাচ্ছে যে, অ্যারেলিস্ট থেকে কোনো উপাদান রিমুভ করার করার কমপ্লেক্সিটি সাধারণভাবে O(n)

অপরদিকে লিংকলিস্টে কোনো উপাদান যুক্ত করার সময় O(1) । লিংকলিস্ট যেহেতু ডাবলিলিংকলিস্ট ডেটা স্ট্রাকচার ব্যবহার করে তাই এর প্রত্যেকটি নোড এর আগের এবং পরের নোডের রেফারেন্স ধরে রাখে। এক্ষেত্রে কোনো উপাদান যুক্ত করার ক্ষেত্রে কোনো কপি বা শিফটিংয়ের প্রয়োজন নেই, বরং শুধুামাত্র দুটি পয়েন্টারের পরিবর্তন ছাড়া।

একইভাবে লিংকলিস্ট থেকে কোনো উপাদান রিমুভ করার ক্ষেত্রে কোনো কপি বা শিফটিংয়ের প্রয়োজন নেই, শুধুমাত্র দুটি রেফারেন্সের পরিবর্তন হয়। তাই এর টাইম কমপ্লেক্সিটি O(1)।

এখন প্রশ্ন হচ্ছে তাহলে কখন কোনটি ব্যবহার করা উত্তম।

সাধারণভাবে আমরা সব কাজেই অ্যারেলিস্ট ব্যবহার করে থাকি। তবে কোনটি করা উচিৎ তা নির্ভর করে কী কাজ করছি তার উপর। যেমন- উপাদান যুক্ত করার চেয়ে অনেক বেশিবার অ্যাকসেস করার প্রয়োজন হলে অথবা আগে থেকেই কতগুলো উপাদান রাখার প্রয়োজন হবে তা সম্পর্কে ধারণা থাকলে অ্যারেলিস্ট ব্যবহার করা উচিত। 

অন্যদিকে আমাদের যদি অ্যাকসেস করার চেয়ে অনেক বেশি উপাদান যুক্ত বা রিমুভ করার প্রয়োজন হয় তাহলে লিংকলিস্ট ব্যবহার করা উত্তম।
-->

Thursday, August 3, 2017

কিভাবে মার্শাল আর্টের ধারণা আপনাকে জাভাতে মাস্টার হতে সাহায্য করে? (How martial art helps you to master Java)

Shu, Ha, Ri এই শব্দ তিনটি এসেছে জাপানি মার্শাল আর্টসের কনসেপ্ট থেকে। এটি সাধারণত কোনো কিছু শেখার ক্ষেত্রে ব্যবহার করা হয়। শব্দ তিনটির অর্থ করলে দাঁড়ায় - 

১. shu (守) "protect", "obey" বা মান্য করা
২. ha (破) "detach", "digress", “break” ভাঙ্গা বা বিচ্ছিন্ন করা 
৩. ri (離) "leave", "separate" আলাদা হওয়া 

জাভা প্রোগ্রামিং শেখার ক্ষেত্রে এই তিনটি ধারণা ব্যবহার করা যেতে পারে। কীভাবে তা নিয়েই এই আলোচনা- 

প্রথমেই সু, অর্থাৎ এখানে প্রত্যেক শিক্ষানবিসদের যে কোনো একটি বই নিয়ে পড়তে শুরু করা উচিৎ(প্রথম পৃষ্টা থেকে একদম শেষ পৃষ্ঠা পর্যন্ত )। বইয়ে যে অনুশীলনীগুলো রয়েছে সেগুলো ভালোভাবে করা। অনেক ক্ষেত্রে বুঝতে অসুবিধা হতে পারে। না বুঝলেও সমস্যা নেই, তবুও বেশি বেশি করে আয়ত্বের মধ্যে নিয়ে আসা। যেমন- জাভাতে কোনো একটি প্রোগ্রাম রান করতে হলে মেইন মেথড লিখতে হয়। মেইন মেথড কেনো লিখতে হয়, এই ব্যাপারটি এখনই না বুঝলে সমস্যা নেই, বরং - 
- কীভাবে মেইন মেথড লিখতে হয়,
- কীভাবে হুয়াইল বা ফর লুপ লিখতে হয়,
- কীভাবে ইফ-এলস বা সুইচ স্ট্যাটমেন্ট লিখতে হয়
- কীভাবে অন্য প্যাকেজ থেকে ক্লাস ইম্পোর্ট করতে হয়,
- কীভাবে স্ক্যানার ব্যবহার করে ইনপুট নিতে হয়,
- কীভাবে কালেকশন ফ্রেমওয়ার্ক ব্যবহার করতে হয়,
- কীভাবে একটি লিস্ট শর্ট করতে হয় এগুলো নিয়ে বেশি বেশি মনোযোগী হওয়া উচিৎ।

অর্থাৎ প্রথমে কী করতে হবে এবং এগুলো কীভাবে করতে হয় এগুলো নিয়ে বেশি মনোযোগী হওয়া উচিৎ, এ পর্যায়ে প্রত্যেকটি বিষয়ের অন্তর্নিহিত তত্ত্ব সম্পর্কে চিন্তা না করলেও চলবে।

দ্বিতীয়ত হা, প্রথম পর্যায়ে যখন জাভাতে প্রোগ্রাম লেখা আয়ত্বের মধ্যে চলে আসে, তখন দ্বিতীয় পর্যায়টি শুরু হয়। এ পর্যায়ে প্রত্যেকটি বিষয়ের অন্তর্নিহিত তত্ত্ব সম্পর্কে চিন্তা শুরু করা উচিৎ। যেমন- 
- জাভাতে কেনো মেইন মেথড স্ট্যাটিক,
- ভার্চুয়াল মেশিন কী?
- জাস্ট ইন টাইম কম্পাইলার কী?
- গার্বেজ কালেকশন কী? এতে কী সুবিধা?
- জাভা প্যাকেজিং বা মডুলারিটি করার সুবিধা কী?
- জাভা কীভাবে মাল্টি-থ্রেডিং হ্যান্ডেল করে বা এর সুবিধা কী ?
- জাভাতে কেনো প্রিমিটিভ টাইপ ও এদের রেপার টাইপ রয়েছে?
- জাভাতে জেনেরিক্স কীভাবে তৈরি করা হয়েছে এবং এটি কীভাবে backward compatible?
- জাভাতে কেনো চেকড এবং আনচেকড এক্সেপশন রয়েছে, এদের আলাদা করণের পেছনের কারণ কী?
- জাভা কী পাস বাই ভ্যালু না পাস বাই রেফারেন্স ইত্যাদি।

এগুলো নিয়ে নানা ধরণের পরীক্ষা-নিরীক্ষা করা, কোড লিখে রান করে দেখা এই পর্যায়ের কাজ। এখানে শিক্ষার্থীরা শুধুমাত্র একটি বই না পড়ে অনেক ধরণের বই পড়তে শুরু করতে পারে। অনেক ব্লগের আর্টিকেল বা ইউটিউব থেকে বিভিন্ন লেকচার বা ওয়ার্কশপ থেকেও শেখা যেতে পারে। 

তৃতীয়ত রু, এই পর্যায়ে শিক্ষার্থী অন্যদের কাছে না শিখে বরং নিজেই নিজের কাছে শিখতে শুরু করে নিজের অনুশীলনের মাধ্যমে। এই পর্যায়ে নিজস্ব স্বকীয়তা তৈরি হয়। যেমন -

- কীভাবে কোড লিখবে, কোডটি ফাংশনাল বা অবজেক্ট ওরিয়েন্টেড হবে কিনা,
- ল্যামডা এক্সপ্রেশন ব্যবহার করা হবে নাকি হবে না,
- জাভা এন্টারপ্রাইজ এডিশন ব্যবহার করবে নাকি স্প্রিং ফ্রেমওয়ার্ক ব্যবহার করবে,
- হাইবারনেট ব্যবহার করবো নাকি Plain JDBC দিয়ে কাজ করা যাবে

ইত্যাদি ব্যাপারে নিজস্ব মতামত তৈরি হয়।

Friday, July 28, 2017

ইনভার্শন অব কন্ট্রোল কী (What is Inversion of Control)?

আমরা বাস্তব জগেতে যখন কোনো কিছু তৈরি করি, তখন কাজগুলোকে ছোট ছোট বিভিন্ন উপাদানে বিভক্ত করে ফেলি। যেমন- গাড়ির ক্ষেত্রে - চাকা, স্টিয়ারিং, chassis, দরজা, ইত্যাদি। এগুলো আরও ছোট ছোট অংশে বিভক্তর করে এদেরকে আলাদা আলাদা ভাবে তৈরি করা হয় এবং পরে একসঙ্গে জুড়ে দেওয়া হয়। 

একইভাবে অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ের মূল ব্যপার হলো বাস্তব জগতের সঙ্গে সাদৃশ্য রেখে কোনো সমস্যা সমাধান করা। এতে অনেকগুলো অবজেক্ট থাকা যারা নিদির্ষ্ট কোনো কাজ করে এবং এগুলোকে একত্রে জুড়ে দিয়ে একটি বড় অবজেক্ট তৈরি করা হয়। প্রত্যেকটি অবজেক্ট একটি বাস্তব জগতের কোনো কিছুর প্রতিরূপ। Airplane অবজেক্ট একটি উড়োজাহাজকে নির্দেশ করতে পারে, সেভাবে একটি Purchase অবজেক্ট একটি বই কেনার বিভিন্ন ডেটা যেমন- দাম, বই অবজেক্টের রেফারেন্স, কত কপি ইত্যাদি নির্দেশ করতে পারে। 

একটি অবজেক্টকে সঠিকভাবে কাজ করতে হলে অনেক সময়ই অন্য এক বা একাধিক অবজেক্টের উপর নির্ভর করতে হয়। যে অবজেক্টের উপর নির্ভর করে কোনো কাজ করতে হয় তাকে অন্য অবজেক্টটির ডিপেডেন্সি বলা হয়। অর্থাৎ A কে কাজ করতে হলে যদি B এর উপর নির্ভর করতে হয়, তাহলে B, A এর ডিপেডেন্সি।

এখন মনে করুন, আপনি একটি ইমেইল অ্যাপ্লিকেশন লিখবেন। এই অ্যাপ্লিকেশনের বেশ কতগুলো অংশ থাকতে পারে।

এক- একটি টেক্সট এডিটরের প্রয়োজন হবে, যা একটি অবজেক্ট। 
দুই- আপনি যে টেক্সটটি লিখবেন, সেটিকে নির্দেশ করতে একটি অবজেক্ট প্রয়োজন হবে।
তিন- ইমেইল সেন্ড করার আগে অবশ্যই আপনি বানানগুলো ঠিক করে নিতে চান, সেক্ষেত্রে বানান শুদ্ধিকরণ একটি অবজেক্টের প্রয়োজন হবে। 
চার- আপনাকে ইমেইলটি পাঠানোর জন্য ইমেইল অ্যাড্রেস প্রয়োজন। এগুলোর জন্য একটি অ্যাড্রেসবুক অবজেক্টের প্রয়োজন হবে, যেখানে আপানার সব ইমেইল অ্যাড্রেসগুলো রয়েছে। 
পাঁচ- একটি ইমেইলার অবজেক্ট যে কিনা ইমেইলটি ইন্টারনেট ব্যবহার করে গন্তব্যে পৌঁছানোর কাজটি করবে। 

ইত্যাদি ..।

তাহলে দেখা যাচ্ছে এই ইমেইলার অবজেক্টটি টেক্সট এডিটর, একচুয়াল টেক্সট, স্পেলচেকার এবং অ্যাড্রেসবুকের উপর নির্ভর করে। এগুলো ইমেইলার অবজেক্টের ডিপেডেন্সি। 

উদাহরণের সুবিধার্থে এবং মূল বিষয়টি সংক্ষিপ্তভাবে বোঝার জন্য মনে করুন, আপনার দুটি অবজেক্ট রয়েছে, একটি ইমেইলার অবজেক্ট যাতে একটি সেন্ড মেথড রয়েছে। আপনি ইমেইল সেন্ড করার আগে বানানগুলো ঠিক করে নিতে চান, সে জন্য প্রয়োজন SpellFixer অবজেক্ট। যেমন- 

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Emailer { 
    private SpellFixer spellFixer; 

    public Emailer() { 
        this.spellFixer = new SpellFixer(); 
    } 

    public void send(String text, String emailAddress) { 
        String fixedText = spellFixer.fixSpelling(text); 

        System.out.println("sending email to: " 
+ emailAddress + " with following text: " + fixedText); 
    } 
}

public class SpellFixer { 

    public String fixSpelling(String text) { 
        //business logic 
        //bla bla 

        return text; 
    } 
} 

এখন আপনি ইমেইলার ক্লাসটির অবজেক্ট তৈরি করে ইমেইল সেন্ড করতে পারবেন। 

1
2
Emailer emailer = new Emailer(); 
emailer.send("bla bla bla", "contact@bazlur.com");


উপরের এই প্রক্রিয়ায় কোনো সমস্যা নেই। এটি একটি সঠিক উপায়। 

তবে আমরা সব সময় যে ইংরেজিতে ইমেল লিখবো, এমনটি হওয়ার কোনো কারণ নেই। কখনো বাংলা বা অন্য কোনো ভাষাতে লিখতে পারি। সেক্ষেত্রে আমাদের এই উপরের সমাধানটি কাজ করবে না। ধরা যাক আমরা বাংলাতে একটি ইমেইল লিখতে চাই এবং এক্ষেত্রে একটি বাংলা স্পেলফিক্সার তৈরি করতে হবে। এক্ষেত্রে - 


1
2
Emailer emailer = new Emailer(); 
emailer.send("bla bla bla", "contact@bazlur.com");

তবে এটি সরাসরি আমাদের ইমেলারে প্লাগ করার কোনো উপায় নেই। যদিও বা আমরা করতে চাই, সেক্ষেত্রে আমাদের নতুন করে আরেকটি ইমেইলার তৈরি করতে হবে -

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class BanglaEmailer { 
    private BanglaSpellFixer spellFixer; 

    public BanglaEmailer() { 
        this.spellFixer = new BanglaSpellFixer(); 
    } 

    public void send(String text, String emailAddress) { 
        String fixedText = spellFixer.fixSpelling(text); 

        System.out.println("sending email to: " + emailAddress + " with following text: " + fixedText); 
    } 

    public static void main(String[] args) { 
        BanglaEmailer banglaEmailer = new BanglaEmailer(); 
        banglaEmailer.send("bla bla bla", "contact@bazlur.com"); 
    } 
} 

যদিও আমাদের ইমেইলার একইরকম, শুধুমাত্র বানান শুদ্ধ করার পক্রিয়া আলাদা হওয়ায় আমাদের দুটি অবজেক্ট তৈরি করতে হচ্ছে। এভাবে যদি আমাদের আরও অন্যান্য ভাষার জন্য লিখতে হয়, তাহলে পক্রিয়াটি আরও কঠিন হয়ে যায়। এছাড়াও এই সমাধানে বেশ কতগুলো সমস্যা রয়েছে -

১. এতে ইমেলারের সাথে স্পেল চেকার খুব টাইটলি কাপলড। এরা আলাদা আলাদাভাবে কাজ করতে পারে না। বাস্তব জগতের প্রত্যেকটি বস্তুই স্বাধীন। সুতরাং বাস্তব জগতের সঙ্গে সাদৃশ্য রইল না। 
২. আলাদা আলাদা স্পেল ফিক্সারের জন্য আলাদা ইমেইলারের প্রয়োজন হচ্ছে,এবং আলাদাভাবে কোড লিখতে হচ্ছে যাতে কোড ডুপ্লিকেশন হচ্ছে। 
৩. আমরা যদি দুটি অবজেক্টকে আলদা আলাদাভাবে টেস্ট করতে চাই, তা সম্ভব হয়ে উঠছে না। এখানে যদিও স্পেল চেকারকে আলাদা করে টেস্ট করা গেলেও, ইমেইলার টেস্ট করতে হলে স্পেল চেকারের অবজেক্ট প্রয়োজন। 
৪. এখানে দুইজন ডেভেলপার বা প্রোগ্রামার আলাদা আলাদাভাবে দুটি ক্লাস নিয়ে কাজ করতে পারবে না। 

এই উপরের সমস্যাগুলো সমাধান করার উপায় রয়েছে- 

নিচের কোডগুলো খেয়াল করুন- 

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public interface SpellFixer { 

    String fixSpelling(String text); 
} 

public class EnglishSpellChecker implements SpellFixer { 
    @Override 
    public String fixSpelling(String text) { 
        // business logic for english spell checker 

        return text; 
    } 
} 

public class BanglaSpellFixer implements SpellFixer { 
    public String fixSpelling(String text) { 
        // business logic for bangla spell fixer 

        return text; 
    } 
} 


public class Emailer { 
    private SpellFixer spellFixer; 

    public void setSpellFixer(SpellFixer spellFixer) { 
        this.spellFixer = spellFixer; 
    } 

    public void send(String text, String emailAddress) { 
        String fixedText = spellFixer.fixSpelling(text); 

        System.out.println("sending email to: " + emailAddress + " with following text: " + fixedText); 
    }  
} 

এখানে প্রথমে একটি ইন্টারেফস তৈরি করা হয়ছে। তারপর দুটি আলাদা ইমপ্লিমেন্টেশন লেখা হয়েছে।

পরবর্তীতে ইমেলার ক্লাসটিতে একটি সেটার মেথড দিয়ে স্পেল চেকারটি ইনজেক্ট করা হয়েছে। অর্থাৎ আমরা কোন স্পেল ফিক্সারটি ব্যবহার করবো তা নির্ভর করছে ইমেলার ইনস্ট্যানসিয়েট করার পর। 


1
2
3
4
5
6
7
8
        SpellFixer spellFixer = new EnglishSpellChecker();
        Emailer emailer = new Emailer();
        emailer.setSpellFixer(spellFixer);
        emailer.send("bla bla bla", "contact@bazlur.com");

        BanglaSpellFixer banglaSpellFixer = new BanglaSpellFixer();
        emailer.setSpellFixer(banglaSpellFixer);
        emailer.send("আমি বাংলার গান গাই", "contact@bazlur.com");

এতে করে অনেকগুলো সুবিধা হলো - 

১. স্পেল ফিক্সার একাধিক থাকলেও ইমেইলারে অবজেক্ট একই থাকছে। 
২. এখানে কোনো কোড ডুপ্লিকেশন নেই।
৩. আমি যখন ইমেইলার টেস্ট করবো তখন যদি কোনো স্পেল চেকার না থাকে,তাহলে একটি মক (Mock) স্পেল চেকার দিয়ে আমার কাজ করে নিতে পারবো। এতে টেস্টিং প্রক্রিয়া অনেক সহজ হয়ে যায়। 
৪. আলদা আলদাভাবে স্পেল চেকার ও ইমেলার তৈরি করা সম্ভব হয়ে যাচ্ছে। 

নিচে একটি টেস্টকোড দেওয়া হলো -

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class EmailerTest { 

    @Test 
    public void testSend() throws Exception { 
        SpellFixer spellFixer = Mockito.mock(SpellFixer.class); 

        String text = "hello world"; 

        when(spellFixer.fixSpelling(text)) 
                .thenReturn(text); 

        Emailer emailer = new Emailer(); 
        emailer.setSpellFixer(spellFixer); 

        emailer.send(text, "contact@bazlur.com"); 
    } 
}

এখানে টেস্টিং ফ্রেমওয়ার্ক JUnit ও Mockito ব্যবহার করা হয়েছে। উপরের টেস্ট কোডটি যদিও কোনো অর্থপূর্ণ টেস্ট করে না, কিন্তু বোঝার জন্য দেওয়া হয়েছে। এখানে SpellFixer ইন্টারফেস ব্যবহার না করে বরং একে মক কর হয়েছে। অর্থাৎ এই পদ্ধতি ব্যবহার করলে যেকোনো ক্লাসকে স্বাধীনভাবে টেস্ট করা যায় এবং টেস্টেবল কোড লেখা যায়। 

উপরের কোডটিতে সেটার ব্যবহার করে অবজেক্ট ইনজেক্ট করা হয়েছে। তবে যদি আমরা কোনো বিশেষ কন্টেইনার যেমন- Spring, Google Guice, picoContainer ইত্যাদি ব্যবহার করে প্রক্রিয়াটি আরও সহজ করে ফেলা যায়। 

1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Emailer { 
    @Autowired 
    private SpellFixer spellFixer; 

    public void send(String text, String emailAddress) { 
        String fixedText = spellFixer.fixSpelling(text); 

        System.out.println("sending email to: " + emailAddress + " with following text: " + fixedText); 
    } 
}


উপরের কোডটিতে শুধু একটি অ্যানোটেশন ব্যবহার করা হয়েছে। এই ইমেইলার ব্যবহার করতে হলে SpellFixer এর ইমপ্লিমেন্টেশনের কোনো ইনস্ট্যান্স নিউ অপারেটর ব্যবহার করে তৈরি করার প্রয়োজন নেই। এখানে স্প্রিং কন্টেইনারের ইনস্ট্যান্স তৈরি করে ইনজেক্ট করে দেয় যখন প্রয়োজন হয়। এতে করে কোডটি আগের থেকে অনেক সংক্ষিপ্ত হয়। 

এই প্রক্রিয়াকে ইনভার্শন অব কন্ট্রোল(Inversion of Control) বলা হয়। অর্থাৎ এটি ব্যবহার করে অবজেক্ট ইনজেক্ট করার কন্ট্রোলটি আমরা একটি কন্টেইনারক দিয়ে দেওয়া হয়। 

এখানে দুটি বিষয় লক্ষ্য করা যায় - 

১. কী করতে চাই
২. কখন করতে চাই

কী করতে চাইকে কখন করতে চাই থেকে আলাদা করে ফেলে আমরা একটি কন্টেইনারকে সেই দায়িত্ব দিয়ে দিতে পারি। কন্টেইনার সিন্ধান্ত নেবে কখন কোন অবজেক্ট ইনস্ট্যানসিয়েট করতে হবে । এই কন্ট্রোলকে আমাদের হাত থেকে কন্টেইনারকে দিয়ে দেওয়ার প্রক্রিয়াকে ইনভারর্সন অব কন্ট্র্রোল বলা হয়। 

Saturday, July 15, 2017

How to make coffee using Java

বলা হয়ে থাকে, প্রোগ্রামার হলো একটি মেশিন যা কফিকে কোডে রূপান্তর করতে পারে। এ থেকেই বোঝা যাচ্ছে কফি অত্যন্ত গুরুত্বপূর্ণ একটি পানীয়।

চলুন তাহলে দেখা যাক কীভাবে এই কফি তৈরি করা যায়?

জাভা দিয়ে কফি তৈরির জন্য প্রয়োজন একটি প্যাটার্ন। এর নাম ডেকোরেটর প্যাটার্ন।

ডেকোরেটর প্যাটার্নের মূল কথা হলো, ডাইনামিক্যালী একটি অবজেক্টে অতিরিক্ত বৈশিষ্ট্যাবলী যুক্ত করা। যেমন- একটি ছবির ফ্রেম। ছবিটি আমাদের মূল অবজেক্ট। এতে একটি ফ্রেম যুক্ত করে একে ডেকোরেট বা অলঙ্কৃত করা। ডেকোরেটার প্যাটার্নে শুরুতে একটি ইন্টারফেস তৈরি করা হয়। এতে একটি মেথড থাকে যা একটি বৈশিষ্ট্য যুক্ত করতে পারে। একটি কনক্রিট ক্লাস থাকে যা ইন্টারফেসটির ইমপ্লিমেন্টেশন। একটি ডেকোরেটর ক্লাস থাকে যাতে ইন্টারফেসটির একটি রেফারেন্স থাকে এবং এটিও ইন্টারফেসটিকে ইমপ্লিমেন্ট করে। এটি সাধরণত একটি অ্যাবস্ট্রাক্ট ক্লাস হয়। এরপর কতগুলো কনক্রিট অ্যাবস্ট্রাক্ট ক্লাস থাকে। একটি উদাহরণ দেওয়া যাক-


1
2
3
public interface Coffee {
    String getIngredient();
}

এর একটি কনক্রিট ক্লাস -


1
2
3
4
5
6
public class CoffeeBean implements Coffee {
    @Override
    public String getIngredient() {
        return "Coffee bean";
    }
}

এবার একটি ডেকোরেটর ক্লাস লেখা যাক -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public abstract class CoffeeDecorator implements Coffee {
    private final Coffee coffee;

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public String addIngredient() {
        return coffee.getIngredient();
    }
}

এবার কিছু কনক্রিট ডেকোরেটর লেখা যাক-


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SaltedCaramelFudge extends CoffeeDecorator {

    public SaltedCaramelFudge(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String addIngredient() {
        return super.getIngredient() + " + Salted caramel fudge";
    }
}
public class SweetenedMilk extends CoffeeDecorator {
    public SweetenedMilk(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String addIngredient() {
        return super.getIngredient() + " + Sweetened Milk";
    }
}
public class VanillaAlmondExtract extends CoffeeDecorator {
    public VanillaAlmondExtract(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String addIngredient() {
        return super.getIngredient() + " + Vanilla/almond extract";
    }
}
public class DarkCookieCrumb extends CoffeeDecorator {
    public DarkCookieCrumb(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String addIngredient() {
        return super.getIngredient() + " + Dark Cookie Crumb";
    }
}

এবার এগুলো দিয়ে কফি তৈরি করা যাক -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class CoffeeApp {

    public static void main(String[] args) {

        Coffee coffee = new VanillaAlmondExtract(new SaltedCaramelFudge(new SweetenedMilk(new CoffeeBean())));
        System.out.println(coffee.getIngredient());
    }
}
Output: 
Coffee bean + Sweetened Milk + Salted caramel fudge + Vanilla/almond extract

একটির কনস্ট্রাক্টরে আরেকটি পাস করে করে এভাবে আমরা ডেকোরেটর প্যাটার্ন ব্যবহার করে ডাইনামিক্যালী কফি তৈরি করতে পারি।

Saturday, July 1, 2017

জাভাতে toString() এর কাজ কী? (Use of toString() Method in java)

জাভাতে toString() এর কাজ কী?

কোনো জাভা অবজেক্টেকে স্ট্রিংয়ে রূপান্তর করতে চাইলে toString() মেথড কল করা হয়। এটি নানা কাজে বিশেষ করে ডিবাগিং কাজে ব্যবহার করা হয়। 

নিচের ক্লাসটি খেয়াল করুন-


1
2
3
4
5
6
7
8
9
public class Employee {
   private String name;
   private int age;

   public Example(String name, int age) {
      this.name = name;
      this.age = age;
   }
}

এবার একটি অবজেক্ট তৈরি করে toString()মেথড কল করা যাক। 


1
2
3
4
public static void main(String[] args) {
   Employee employee = new Employee("Rahim", 27);
   System.out.println(employee.toString());
}


এটিকে রান করলে -


যেহেতু Employee ক্লাসটিতে toString() মেথডটি ওভাররাইড করা হয়নি, তাই এটি Object ক্লাসের toString()মেথডটি কল হয় যা ক্লাস নামের সঙ্গে হ্যাশকোড যুক্ত করে রিটার্ন করেন। 

অর্থপূর্ণ কোনো কিছু রিটার্ন করতে হলে চলুন এটিকে ওভাররাইড করি।


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Employee {
   private String name;
   private int age;

   public Employee(String name, int age) {
      this.name = name;
      this.age = age;
   }

   @Override
   public String toString() {
      
      return name + ": " + age;
   }

   public static void main(String[] args) {
      Employee employee = new Employee("Rahim", 27);
      System.out.println(employee.toString());
   }
}

এটিকে রান করলে আউটপুট পাওয়া যাবে -

Rahim: 27

Saturday, June 24, 2017

জাভা অবজেক্ট সিরিয়ালাইজেশন (Java serialization)

সিরিয়ালাইজেশন (Serialization) 

আমরা জানি যে ক্লাস থেকে অবজেক্ট তৈরি করা হয়। ক্লাসে মূলত একটি অবজেক্টে কী কী প্রোপ্রার্টিজ থাকবে এবং এটি কী কী কাজ করতে পারবে তার বর্ণনা থাকে। কিন্তু যখন নিউ(new) অপারেটর ব্যবহার করে একে অবজেক্ট তৈরি করা হয়, তখন এটি একটি জীবন্ত প্রাণির মতো অবজেক্টে পরিণত হয়। একটি জীবন্ত প্রাণির যেমন অনেকগুলো নিজস্ব ও অনন্য (unique) বৈশিষ্ট্য থাকে (যেমন- নাম, বয়স, ডিএনএ সিকুয়েন্স ইত্যাদি), তেমনি একটি অবজেক্টের একইরকম অনন্য পরিচয় (unique identity) থাকে। প্রত্যেকটি অবজেক্ট কিছু না কিছু স্টেট(state) বা ডেটা থাকে। প্রত্যেকটি অবজেক্টের একটি জীবন চক্র থাকে (life cycle)। এটি নিউ অপারেটর দিয়ে শুরু হয় এবং গারবেজ কালেক্টর(garbage collector) দিয়ে শেষ হয়। এই শুরু এবং শেষ হওয়ার মধ্যবর্তী অবজেক্টের কোনো অবস্থাকে বাইনারী ফরমেটে স্টোর করা যায় এবং সেই একই অবস্থা থেকে পুনর্গঠিত করা যায়। 

অবজেক্টের এই কোনো অবস্থাকে বাইনারী ফরমেটে রূপান্তর করার প্রক্রিয়াকে সিরিয়ালাইজেশন(serialization) বলা হয়। আবার এই বাইনারী ফরমেট থেকে অবজেক্টে পুনর্গঠিত করার প্রক্রিয়াকে ডিসিরিয়ালাইজেশন(deserialization) বলা হয়। 

সাধারণত দুটি কাজে এই সিরিয়ালাইজেশনের দরকার হয় - 
১. অ্যাপ্লিকেশনের কোনো প্রয়োজনে অবজেক্টের অবস্থানকে স্থায়িভাবে সংরক্ষণ করার প্রয়োজন হতে পারে। যেমন- ডেটাবেইজে সংরক্ষণ। 
২. একটি অজবেক্টেকে একটি কম্পিউটার থেকে অন্য কম্পিউটারে পাঠোনোর প্রয়োজন হতে পারে। 

এবার তাহলে দেখা যাক কীভাবে অবজেক্টকে সিরিয়ালাইজ করা যায়। 

সব অবজেক্টকেই সিরিয়ালাইজ করা যায় না। কোনো অবজেক্টকে সিরিয়ালাইজ করতে হলে, সেই অবজেক্টের ক্লাসকে অবশ্যই java.io.Serializable ইন্টারফেসকে ইমপ্লিমেন্ট করতে হবে। এই ইন্টারফেসটিতে কোনো মেথড নেই। এটি মূলত একটি মার্কার ইন্টারফেইস। 

import java.io.Serializable;

public class Person implements Serializable {
      private String name;
      private int age;
      // more properties & methods 
}

উপরের Person ক্লাসটি Serializable ইন্টরফেসটিকে ইমপ্লিমেন্ট করে। এর অর্থ হলো, এই ক্লাসের যেকোনো অবজেক্টকে সিরিয়ালাইজ করা যাবে। 

তবে যদি কোনো ক্লাস যদি এই ইন্টারফেসকে ইমপ্লিমেন্ট না করে, এবং সেই ক্লাসের অবজেক্টকে সিরিয়ালাইজ করার চেষ্টা করা হয়, তাহলে জাভা রানটাইম NotSerializableException থ্রু করবে।

সিরিয়ালাইজেশন প্রক্রিয়া: 

প্রথমে আমাদের একটি অবজেক্ট তৈরি করতে হবে। আমরা অবজেক্টের বাইনারি ফরমেটটি একটি ফাইলে সংরক্ষণ করতে চাই। এজন্য আমাদের একটি আউটপুট স্ট্রিম লাগবে- সেক্ষেত্রে যা FileOutputStream । একে একটি ObjectOutputStream দিয়ে wrap করে এর writeObject() মেথডটি কল করলেই অবজেক্টটি সিরিয়ালাইজ হয়ে আউটপুট স্ট্রিমে রাইট হবে। 

1
2
3
4
5
6
7
8
9
public static void serializeToDisk(String fileName, List<OrderLine> orders) {
        File file = new File(fileName);

        try (ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file))) {
            outputStream.writeObject(orders);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

উপরের মেথডটি একটি অর্ডার লিস্টকে একটি নির্দিষ্ট ফাইলে সংরক্ষণ করে। এখানে আর্গুমেন্ট থেকে প্রাপ্ত ফাইলের নাম দিয়ে একটি ফাইল তৈরি করা হয়েছে। তারপর ট্রাই-রিসোর্স ব্যবহার করে আউটপুট স্ট্রিম তৈরি করা হয়েছে। এবং ট্রাই ব্লকের ভেতরে অর্ডার লিস্টকে writeObject() মেথড দিয়ে সিরিয়ালাইজ করা হয়েছে। 

ডিসিরিয়ালাইজেশন প্রক্রিয়া 

সিরিয়ালেশনের মূল উদ্দেশ্য হচ্ছে সিরিয়ালাইজ কৃত অবজেক্টিকে আবার পুনর্গঠিন করে ব্যবহার করা। এই প্রক্রিয়াটি উপরের প্রক্রিয়াটির মতোই সহজ। এক্ষেত্রে একটি ইনপুট স্ট্রিমের প্রয়োজন হয়। 

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public static List<OrderLine> deserializeFromDisk(String fileName) {
        File file = new File(fileName);

        try (ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file))) {

            return (List<OrderLine>) inputStream.readObject();

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }


উপরের মেথডে আর্গুমেন্ট থেকে প্রাপ্ত থেকে ফাইলের নাম থেকে একটি ফাইল তৈরি করা হয়েছে। এরপর ট্রাই-রিসোর্স স্টেটমেন্ট ব্যবহার করে ইনপুট স্ট্রিম তৈরি করা হয়েছে। ট্রাই ব্লকের মধ্যে ইনপুট স্ট্রিমে মেথড readObject() ব্যবহার করে সিরিয়ালাইজকৃত অবজেক্টটি পুনর্গঠন করা হয়েছে। 


Tuesday, June 20, 2017

ফাংশন কম্পোজিশন (Function Composition)

ফাংশন কম্পোজিশন (Function Composition)

অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ে একটি অবজেক্টের সঙ্গে এক বা একাধিক অবজেক্ট যুক্ত করে (অ্যাসোসিয়েশন বা ইনহেরিটেন্সের মাধ্যমে) নতুন অবজেক্ট তৈরি করা যায়। একইভাবে জাভাতে java.util.function.Function ফাংশনাল ইন্টারফেস একাধিক ফাংশন কম্বাইন করে নতুন পুনঃব্যবহারযোগ্য (reusable) ফাংশন তৈরি করা যায়। একেই ফাংশন কম্পোজিশন বলে।

ফাংশন কম্পোজিশনের জন্য এই ফাংশন ইন্টারফেসে দুটি ডিফল্ট মেথড রয়েছে। এগুলো হলো - compose() এবং
andThen()

এই দুটি মেথড সম্পর্কে জানার আগে আমারা গাণিতিক ফাংশন নিয়ে একটু আলোচনা করা নেই।

ফাংশন মূলত এক ধরণের ম্যাপিং। এটি এক বা একাধিক ইনপুট নিয়ে অন্য একটি ভ্যালু আউটপুট হিসেবে দেয়। মনে করি একটি ফাংশন-




তাহলে যে কোনো ইন্টিজার ইনপুটের জন্য এর ম্যাপিং হবে -



















এখন মনে করি আরেকটি ফাংশন -




যে কোনো ইন্টিজার ইনপুটের জন্য এর ম্যাপিং হবে -



















এখন আমরা দুটি ফাংশনের ম্যাপিং জানি। এই দুটি ফাংশন কম্বাইন করে আমরানির্ণয় করতে পারি।


একে আমরা এভাবে লিখতে পারি -


এটি বের করার স্টেপগুলো নিচে সহজভাবে দেওয়া হলো-












তাহলে প্রথমে আমাদের g(1) বের করতে হবে। এটি আমরা উপরের প্রথম ফাংশনের চার্ট থেকে বের করতি পারি কিংবা অংক করেও বের করতে পারি -



এখন


h(-3) এর মান আমরা দ্বিতীয় ফাংশনের চার্ট থেকে বের করতে পারি কিংবা অংক করেও বের করতে পারি। -




এভাবে আমরা দুটি ফাংশন কম্বাইন করে একটি ফলাফল পেলাম।

একইভাবে এই দুটি ফাংশনকে যদি আমরা ল্যমাডা এক্সপ্রেশন দিয়ে প্রকাশ করি তাহলে হবে -


Function g = x -> (int) (Math.pow(x, 2)) - 4;
Function h = y -> 3 * (int) (Math.pow(y - 1, 2)) - 5;


এই দুটি ফাংশনকে কম্বাইন করার জন্য আমরা ফাংশন ইন্টারফেসের ডিফল্ট মেথড compose()
ব্যবহার করে আরেকটি নতুন ফাংশন তৈরি করতে পারি।


Function hog = h.compose(g);

এখন এই ফাংশনে যদি 1 ইনপুট দেওয়া হয়, তাহলে আউটপুট আসবে 43

Integer result = hog.apply(1);
System.out.println(result);

output : 
43 


এভাবে আমরা গাণিতিক ফাংশনের মতো জাভার ফাংশনাল ইন্টারফেস Function ব্যবহার করতে পারি।

এই ফাংশনে দুটি ডিফল্ট মেথডই এই ফাংশনের কম্পোজিশনের জন্য ব্যবহার করা হয়। তবে এদের কাজ করার ক্রমের পার্থ্ক্য রয়েছে।


Function hog = h.compose(g);
Function hAndThenG = h.andThen(g);


উপরের দুটি উদাহরণের মধ্যে প্রথমটিতে compose() মেথড এবং দ্বিতীয়টিতে andThen() ব্যবহার করা হয়েছে। প্রথমটির ক্ষেত্রে আগে g ল্যামডা এক্সপ্রেশনটি ইভ্যালুয়েট হবে এবং এর আউটপুট h এর ল্যামডা এক্সপ্রেশনে আর্গুমেন্ট হিসেবে দেওয়া হবে। দ্বিতীয়টির ক্ষেত্রে প্রথমে h এর ল্যামডা এক্সপ্রেশনটি ইভ্যালুয়েট হবে এবং এর আউটপুট g এর ল্যামডা এক্সপ্রেশনে আর্গুমেন্ট হিসেবে যাবে।
-->