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