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 এর ল্যামডা এক্সপ্রেশনে আর্গুমেন্ট হিসেবে যাবে।
-->