Wednesday, April 19, 2017

ল্যামডা এক্সপ্রেশনঃ পর্ব দুই (Lambda Expression: Part 2)

আগে পর্বে জাভাতে ল্যামডা এক্সপ্রেশন কী এবং কীভাবে ব্যবহার করা হয় তার একটি প্রস্তাবনা দেওয়া হয়েছে। এবার আরও ভেতরে প্রবেশ করা যাক-

খুব সহজে করে সংজ্ঞায়িত করা যায় এভাবে - ল্যামডা এক্সপ্রেশন হলো অ্যানোনিমাস ফাংশন যা অন্য মেথডে আর্গুমেন্ট হিসেবে পাস করা যায় বা ভ্যারিয়েবলে অ্যাসাইন করা যায়। 

আমরা সংজ্ঞা থেকে এর তিনটি বৈশিষ্ট্যকে আলাদা করতে পারি -

১. অ্যানোনিমাস- এদের নাম নেই। 
২. ফাংশন- এটিতে মেথডের মতো প্যারামিটার লিস্ট থাকে, রিটার্ন টাইপ থাকে এবং এক্সেপশন লিস্ট থাকতে পারে। তবে যেহেতু এগুলো কোনো ক্লাসের সাথে সংযুক্ত নয় তাই এগুলোকে ফাংশন বলা হয়। 
৩. পাসড অ্যারাউন্ড- এগুলোকে কোনো মেথডের আর্গুমেন্ট হিসেবে পাস করা যায় বা ভ্যারিয়েবলে অ্যাসাইন করা যায়। 

তবে ল্যামডা এক্সপ্রেশনের আরকটি বৈশিষ্ট্য হলো, সংক্ষিপ্ত। এতে অনেক কম লাইন কোড লিখে একই কাজ করা যায়। এতে বয়লারপ্লেট কোড কম থাকে। 

এবার ল্যামডা এক্সপ্রেশনের সিনট্যাক্স দেখা যাক। ল্যামডা এক্সপ্রেশনের তিনটি অংশ থাকে- 

১. প্যারামিটার লিস্ট- এতে শুরুতে কতগুলো প্যারামিটার থাকে। প্যরামিটারগুলোতে টাইপ থাকতে পারে। তবে অনেক্ষেত্রেই টাইপ ইনফরমেশন দেওয়ার প্রয়োজন পরে না। কম্পাইলার বুঝে নিতে পারে। একাধিক প্যরিমিটার থাকলে এগলোকে কমা দিয়ে আলাদা করতে হয় এবং প্যারামিটারগুলো একটি প্যারেন্থেসিসের মধ্যে থাকে। তবে একটি প্যারামিটার থাকলে প্যারেন্থেসিস দেওয়ার প্রয়োজন হয় না। 
২. অ্যারো সাইন - অ্যারো ( -> ) সাইন প্যারামিটার লিস্টকে বডি থেকে আলাদা করে। এটি দুটি ক্যারেক্টার (-) ড্যাশ ও (>) রাইট অ্যাঙ্গেল ব্রাকেট সংযুক্ত করে লেখা হয়। 
৩. বডি - যেখানে মূল কোড অংশটুকু থাকে।

কতগুলো উদাহরণ দেখা যাক - 

1
2
3
(Contact contact1, Contact contact2) -> {
          return contact1.getName().compareTo(contact2.getName());
};

তবে একে সংক্ষিপ্ত করে এক লাইনেও লেখা যায়- 

1
(Contact contact1, Contact contact2) -> contact1.getName().compareTo(contact2.getName());

একে ভ্যারিয়েবলে অ্যাসাইন করা যায়- 

1
Comparator contactComparator = (Contact contact1, Contact contact2) -> contact1.getName().compareTo(contact2.getName());

আরও কয়েকটি উদাহরণ- 


1
2
3
(String s) -> {
        return s.toUpperCase();
}

তবে একে সংক্ষি্প্ত করে লিখলে -


1
(String s) -> s.toUpperCase()

সুতরাং ল্যামডা এক্ষপ্রেশন দু রকম হতে পারে -

(parameters) → expression

অথবা 

(parameters) → {statements;}

বডিতে একাধিক স্ট্যাটমেন্ট থাকলে কার্লি ব্রেসের প্রয়োজন হয়।

Saturday, April 15, 2017

ল্যামডা এক্সপ্রেশনঃ পর্ব এক (Lambda Expression: Part 1)

অনেকগুলো জনপ্রিয় প্রোগ্রামিং ল্যাংগুয়েজেই ল্যামডা এক্সপ্রেশন রয়েছে। যেমন- জাভাস্ক্রিপ্ট, পাইথন, গ্রুভি, স্ক্যালা, সি শার্প, সি++ ইত্যাদি। জাভাতে এই বিশেষ ফিচারটি অনেকদিন অনুপস্থিত থাকায় জাভা প্রোগ্রামারদের মধ্যে মনোকষ্টের কমতি ছিল না। ২০০৬ সালে জাভা পাঁচ রিলিজ হওয়ার পর এই ফিচারটি নিয়ে আলোচনা শুরু হলেও এটি খুব একটা আলোর মুখ দেখে নি। অনেকেই বলতে শুরু করে "Java is the new COBOL”। তবে জাভাকে ওরাকল কিনে নেওয়ার পর এই অবস্থার পরিবর্তন হতে শুরু করে এবং অবশেষে প্রোগ্রামারদের প্রবল মনোবেদনা ঘুচিয়ে ২০১৪ সালে রিলিজ হয় জাভা আট যাতে এই বিশেষ এবং খুবই গুরুত্বপূর্ণ ফিচারটি সংযুক্ত করা হয়।

চলুন তাহলে এই বিশেষ প্রোগ্রামিং ফিচার, ল্যামডা এক্সপ্রেশন নিয়ে আলোচনা করা যাক।


ল্যামডা এক্সপ্রেশনঃ

ল্যামডা টার্মটি এসেছে ল্যম্বডা ক্যালকুলাস (Lambda Calculus) থেকে। এটি গণিতিক যুক্তি ও কম্পিউটার সাইন্সে ব্যবহৃত এক ধরণের ফর্মাল সিস্টেম যাতে বিভিন্ন ধরণের কম্পিউটিশন বিভিন্ন ভ্যারিয়েবলের মাধ্যমে প্রকাশ করা হয়। তবে এই আমাদের এই আলোচনায় তাত্বিক দিকটি পরিহার করে বরং সাধারণভাবে এর আচরণ ও ব্যবহার নিয়ে কথা বলা যাক। 

সহজকরে বলা যেতে পারে যে, ল্যমডা এক্সপ্রেশন হলো একটি ছোট কোড যা কোনো কাজ করে থাকে। উদহারণ-

(int x) → {return x +1;}

উপরের কোডটি একটি ল্যামডা এক্সপ্রেশনের উদাহরণ। এটি দেখে মনে হচ্ছে একটি মেথড কিন্তু এর কোনো নাম নেই। এতে একটি মেথডে যা কিছু থাকে তার পায় সবই রয়েছে, যেমন – প্যারামিটার, মেথড বডি। তবে এতে রিটার্ন টাইপ দেখা যাচ্ছে না। রিটার্ন টাইপ এক্ষেত্রে কম্পাইলার বুঝে নিতে পারে, কারণ এর রিটার্ন স্ট্যাটমেন্টে দেখা যাচ্ছে যে এটি একটি ইন্টিজার রিটার্ন করছে। তাহলে একে বলতে পারি নাম ছাড়া মেথড বা অ্যানোনিমাস মেথড।


অ্যানোনিমাস ইনার ক্লাস ও ল্যাম্ডা এক্সপ্রেশন

জাভা অ্যানোনিমাস ইনার ক্লাস তৈরি করতে দেয় এবং এর বিভিন্ন রকম ব্যবহার রয়েছে। এটিও একধরণের ক্লাস তবে এর কোনো নাম নেই। এটি সাধারণত অন্য একটি ক্লাসের মধ্যে তৈরি করা হয় বলে একে অ্যানোনিমাস ইনার ক্লাস বলা হয়। অ্যানোনিমাস ক্লাস দুই ভাবে তৈরি করা যায়- ইন্টারফেস থেকে এবং একটি ক্লাস থেকে।


1
2
3
public interface Filterable {
     boolean apply(Object obj);
}

উপরের ইন্টারফেইসটি থেকে একটি অ্যানোনিমাস ইনার ক্লাস তৈরি করা যেতে পারে।


1
2
3
4
5
6
7
new Filterable() {
    @Override
    public boolean apply(Object object) {
 // other logic 
        return false;
    }
};

এখানে ইন্টারফেস থেকে একটি ইনার ক্লাস তৈরি করা হয়েছে। এখানে লক্ষ করুন, ইন্টারফেসের সঙ্গে একটি নিউ অপারেটর ব্যবহার করা হয়েছে এবং কার্লি ব্রেসের মধ্যে ইন্টারফেইসটির মেথডটি ইম্প্লিেমেন্ট করা হয়েছে। অর্থাৎ অ্যানোনিমাস ইনার ক্লাস তৈরি করতে হলে নিউ অপারেটর ব্যবহার করতে হয়।

একইভাবে ক্লাস থেকেও অ্যানোনিমাস ইনার ক্লাস তৈরি করা যায়। যে ক্লাস থেকে অ্যানোনিমাস ক্লাস তৈরি করা হবে সেই ক্লাসটিকে অ্যানোনিমাস ইনার ক্লাস ইনহেরিট করে। উদাহরণ-


1
2
3
4
5
new Person() {
    public void doSomething() {
        System.out.println("doing something");
    }
};

এখানে Person ক্লাস ব্যবহার করে একটি অ্যানোনিমাস ক্লাস তৈরি করা হয়েছে। তবে এই পদ্ধতি নিয়ে এই অনুচ্ছেদে এর বেশি আলোচনা হবে না।

এবার চলুন এই অ্যানোনিমাস ইনার ক্লাসের সুবিধা নিয়ে আলোচনা করা যাক -

মনে করুন, আপনার কাছে একটি কন্টাক্ট লিস্ট আছে। আপনি এই কন্টাক্ট লিস্ট থেকে নানাভাবে কন্টাক্টগুলোকে ফিল্টার করতে চান।

আপনার কন্টাক্ট ক্লাসটি দেখতে এরকম-


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
42
43
44
45
46
47
48
49
50
51
public class Contact {
    enum Sex {
        MALE, FEMALE
    }

    private String name;
    private String emailAddress;
    private String phoneNumber;
    private int age;
    private Sex sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Sex getSex() {
        return sex;
    }

    public void setSex(Sex sex) {
        this.sex = sex;
    }
}

এখন আপনি একটি মেথড লিখতে পারেন, যা আপনার লিস্ট থেকে যাদের বয়স ১৮ থেকে ২৫ এর মধ্যে তাদেরকে আলাদা করবে। মেথডটি এভাবে লেখা যেতে পারে -



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.util.ArrayList;
import java.util.List;

public class ContactService {
    public List<Contact> findContactAgedBetween18And25(List<Contact> contactList) {
        List<Contact> contacts = new ArrayList<>();
        
        for (Contact contact : contactList) {
            if (contact.getAge() >= 18 && contact.getAge() <= 25) {
                contacts.add(contact);
            }
        }
        return contacts;
    }
}

এক্ষেত্রে এই মেথড প্রথমে কন্টাক্ট লিস্ট থেকে একটি ফর লুপ এবং একটি ইফ স্ট্যাটমেন্ট ব্যবহার করে প্রত্যেকটি কন্টাক্ট পরীক্ষা করে দেখছে যে এদের বয়স ১৮ থেকে ২৫ এর মধ্যে কিনা। তারপর মেথডের মধ্যে একটি লোকাল ভ্যারিয়েলে সেগুলো রাখছে এবং লুপ শেষ হয়ে গেলে রিটার্ন করছে।

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


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public List<Contact> findContactMaleAgedBetween18And25(List<Contact> contactList) {
    List<Contact> contacts = new ArrayList<>();

    for (Contact contact : contactList) {
        if (contact.getAge() >= 18
                && contact.getAge() <= 25
                && contact.getSex() == Contact.Sex.MALE) {
            contacts.add(contact);
        }
    }

    return contacts;
}

এভাবে যদি মহিলাদের আলাদা করতে চান তাহলে -


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public List<Contact> findContactFemaleAgedBetween18And25(List<Contact> contactList) {
    List<Contact> contacts = new ArrayList<>();

    for (Contact contact : contactList) {
        if (contact.getAge() >= 18
                && contact.getAge() <= 25
                && contact.getSex() == Contact.Sex.FEMALE) {
            contacts.add(contact);
        }
    }

    return contacts;
}

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

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


1
2
3
interface FilterCriteria {
    boolean match(Contact contact);
}

এখন আমরা একটি মেথড লিখবো যেখানে এই ইন্টারফেইসটি প্যারামিটার হিসেবে থাকবে।


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public List<Contact> findContacts(List<Contact> contactList, FilterCriteria criteria) {
    List<Contact> contacts = new ArrayList<>();

    for (Contact contact : contactList) {
        if (criteria.match(contact)) {
            contacts.add(contact);
        }
    }

    return contacts;
}

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


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import java.util.ArrayList;
import java.util.List;

public class ContactApp {
    public static void main(String[] args) {
        List<Contact> contactList = new ArrayList<>(); // lets assume we have that list
        ContactService contactService = new ContactService();
        List<Contact> contactAged18To25 = contactService.findContacts(contactList, new FilterCriteria() {
            @Override
            public boolean match(Contact contact) {
                return contact.getAge() >= 18 && contact.getAge() <= 25;
            }
        });
    }
}

উপরের মেথডটিতে আর্গুমেন্ট হিসেবে একটি অ্যানোনিমাস ইনার ক্লাস দেওয়া হয়েছে যাতে আমরা match() মেথডের মূল লজিকটুকু লিখেছি। এভাবে আমাদের যদি এখন এই ১৮ থেকে ২৫ বছর বয়সের কন্টাক্টগুলো থেকে শুধুমাত্র মহিলাগুলোকে আলাদা করতে চাই তাহলে কন্টাক্ট সার্ভিসে(ContactService) নতুন কোনো মেথড যুক্ত করতে হবে না। শুধুমাত্র কল করার সময় আমাদের কোডের বিহেবিয়ারটুকু আর্গুমেন্ট হিসেবে পাস করলেই হয়ে যাচ্ছে।


1
2
3
4
5
6
7
8
List<Contact> contactAged18To25Female = contactService.findContacts(contactList, new FilterCriteria() {
    @Override
    public boolean match(Contact contact) {
        return contact.getAge() >= 18
                && contact.getAge() <= 25
                && contact.getSex() == Contact.Sex.FEMALE;
    }
});

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

তবুও উপরের কোডটিতে খেয়াল করলে দেখা যাবে যে বেশ কিছু কোড বারবার লিখতে হচ্ছে।

চিত্র: প্রয়োজনীয় কোড লজিক

এই কোডটিতে শুধুমাত্র লাল রঙ্গের অংশটুকু আমাদের প্রয়োজনীয় কোড, বাকিটুকু অর্থাৎ নিউ অপারেটর থেকে শুরু করে, ওভারাইড অ্যানোটেশন, পাবলিক কিওয়ার্ড, রিটার্ন টাইপ, মেথডের নাম ইত্যাদি একই রয়ে গেছে পত্যেকবার কল করার সময়। এই কোডগুলোকে boilerplate কোড বলা হয়।

তবে জাভা ৮ ল্যাম্বডা এক্সপ্রশন আসার সুবাধে এই boilerplate কোডটুকুকেও বাদ দেওয়া যায়। উদাহরণ -


1
2
3
4
5
6
List<Contact> contactAged18To25Female = contactService.findContacts(contactList,
        (Contact contact) -> {
            return contact.getAge() >= 18
                    && contact.getAge() <= 25
                    && contact.getSex() == Contact.Sex.FEMALE;
        });

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

এই প্যারামিটাইরাইজড কোডটুকু আলাদা করে লিখলে -


1
2
3
4
5
6
(Contact contact) -> {
    
    return contact.getAge() >= 18
            && contact.getAge() <= 25
            && contact.getSex() == Contact.Sex.FEMALE;
};

যা শুরুতে ল্যাম্বডা নিয়ে কথা বলার সময় যে উদাহরণ দেওয়া হয়েছিল তার সঙ্গে মিলে যায়।

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

আরও একটি ব্যবাহার দেখা যাক-


1
2
3
4
5
6
7
File root = new File("./");
File[] files = root.listFiles(new FileFilter() {
    @Override
    public boolean accept(File pathname) {
        return pathname.isFile();
    }
});

অনেক সময় আমাদের ফাইল লিস্টিং করতে হয়, অর্থাৎ একটি ফোল্ডারে যে ফাইলগুলো  রয়েছে তা বিভিন্নভাবে ফিল্টার করে তার লিস্ট করা যায়। এক্ষেত্রে উপরের কোডটি রুট ফোল্ডারে যে ফাইলগুলো ডিরেক্টরী নয় শুধুমাত্র ফাইল(জাভাতে ইউনিক্স সিস্টেম অনুসরণ করে ফাইল এবং ডিরেক্টরী দুটুই একটি ক্লাস java.io.File দিয়ে অ্যাবস্ট্র্যাক্ট করা হয়েছে), সেগুলোকে লিস্ট করবে।

তবে উপরের কোডটুকু সংক্ষিপ্ত করে ল্যামডা এক্সপ্রেশন ব্যবহাv করে এভাবে লেখা যায় -


1
File[] files = file.listFiles(pathname -&gt; pathname.isFile());

কিংবা আরও সংক্ষেপে -


1
File[] files = file.listFiles(File::isFile); //method reference

এখানে মেথড রেফারেন্স ব্যবহার করা হয়েছে।

এই আর্টিক্যাল এ শুধুমাত্র ল্যামডা এক্সপ্রেশনের সাধারণ ব্যবহার দেখানো হলো। পরিবর্তীতে পর্বে এ নিয়ে আরও বিস্তারিত থাকবে।

Saturday, April 8, 2017

ইনমেমোরী ফাইল সিস্টেম (In memory file system in java)

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

অনেক সময়ই বিভিন্ন ইন্টারভিউতে ইন মেমোরী ফাইল সিস্টেম তৈরি করতে বলে। প্রশ্নটি এমন থাকে যে, একই ইন মেমোরী ফাইল সিস্টেম তৈরি রকতে হলে আপনি কীভাবে ডিজাইন করবেন এবং কী কী ডেটা স্ট্রাকচার ব্যবহার করবেন। সম্ভব হলে কিছু কোড লিখে দেখান। (Explain the data structures and algorithm that  you would use to design an in-memory file system. Illustrate with an example in code if possible. )

অনেকই ফাইল সিস্টেমের নাম শুনেই ভয় পেয়ে যায়, মনে করে এটি অনেক লো লেভেল (low level) অর্থাৎ অপারেটিং সিস্টেমের সঙ্গে সম্পর্কিত। কিন্তু সহজ-সরল ডিজাইন কিন্তু খুব কঠিন কিছু নয়। অবজেক্ট ওরিয়েন্টেড ডিজাইন ব্যবহার করেই এটি সমাধান করে ফেলা যায়। 

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


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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public abstract class Node implements Comparable<Node> {
    private Directory root;
    private String name;
    private LocalDateTime created;
    private LocalDateTime lastUpdated;
    private LocalDateTime lastAccessed;

    public Node(String name) {
        this.name = name;
        this.created = LocalDateTime.now();
        this.lastUpdated = LocalDateTime.now();
        this.lastAccessed = LocalDateTime.now();
    }

    public boolean isDirectory() {
        return this instanceof Directory;
    }

    public String getPath() {
        return root != null ? root.getPath() + "/" + name : name;
    }

    public Node getParent() {
        return root;
    }

    public abstract long getLength();

    public String getName() {
        return name;
    }

    @Override
    public int compareTo(Node o) {
        return this.getName().compareTo(o.getName());
    }

    public void setRoot(Directory root) {
        this.root = root;
    }

    public LocalDateTime getCreated() {
        return created;
    }

    public LocalDateTime getLastUpdated() {
        return lastUpdated;
    }

    public LocalDateTime getLastAccessed() {
        return lastAccessed;
    }

    @Override
    public String toString() {
        return "root=" + root +
                ", \nname='" + name + '\'' +
                ", \ncreated=" + created +
                ", \nlastUpdated=" + lastUpdated +
                ", \nlastAccessed=" + lastAccessed;
    }
}


এখানে কম্পেয়ারেবল ইন্টারফেইস ইমপ্লিমেন্ট করার কারণ হলো, যাতে আমরা সর্ট করতে পারি।

প্রত্যেকটি ফাইলের একটি করে রুট থাকে। রুট না থাকলে সে নিজেই রুট। এই নোডকে এক্সটেন্ড করে একটি ফাইল তৈরি করে ফেলা যাক -


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
public class File extends Node {
    private String content; // for simplicity

    public File(String name, String content) {
        super(name);
        this.content = content;
    }

    public File(String name) {
        super(name);
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public long getLength() {
        if (content != null) {
            return content.getBytes().length;
        }
        return 0;
    }
}


প্রত্যেকটি ফাইল কিছু কন্টেন্ট রাখে। সহজভাবে বুঝানোর জন্য এতে আপরা স্ট্রিং রেখেছি।
এবার ডিরেক্টরী স্ট্রাকচার তৈরি করা যাক -


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
public class Directory extends Node {
    private Set<Node> nodes;
    public Directory(String path) {
        super(path);
        nodes = new TreeSet<>();
    }

    public void add(Node node) {
        node.setRoot(this);
        nodes.add(node);
    }

    public Set<Node> getNodes() {
        return nodes;
    }

    @Override
    public long getLength() {
        long length = 0;
        for (Node node : nodes) {
            length += node.getLength();
        }
        return length;
    }

    public int numberOfFiles() {
        int count = 0;
        for (Node node : nodes) {
            if (node instanceof Directory) {
                count++;/// Directory counts as a file
                Directory directory = (Directory) node;
                count += directory.numberOfFiles();
            } else if (node instanceof File) {
                count++;
            }
        }

        return count;
    }
}


প্রত্যেকটি ডিরেক্টরীতে অারও অনেকগুলো করে ফাইল বা ডিরেক্টরী থাকতে পারে। এক্ষেত্রে ডিরেক্টরীতে একটি ট্রিসেটে (TreeSet) রাখা হয়েছে যা সেই ফাইল বা ডিরেক্টরীগুলো ধরে রাখে।

ডিরেক্টরী স্ট্রাকচারকরে সুন্দর করে প্রিন্ট করার জন্য কতগুলো মেথড যুক্ত করা যেতে পারে । যেমন-


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
public void printTree() {
    int indent = 0;
    StringBuilder sb = new StringBuilder();
    printDirectoryTree(this, indent, sb);
    System.out.println(sb.toString());
}

private void printDirectoryTree(Node node, int indent, StringBuilder sb) {
    sb.append(getIndentString(indent));
    sb.append("+--");
    sb.append(node.getName());
    sb.append("/");
    sb.append("\n");
    if (node.isDirectory()) {
        Directory directory = (Directory) node;
        for (Node file : directory.getNodes()) {
            printDirectoryTree(file, indent + 1, sb);
        }
    }
}

private static String getIndentString(int indent) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < indent; i++) {
        sb.append("|  ");
    }
    return sb.toString();
}


এবার একটি মেইন মেথড থেকে এগুলোকে ব্যবহার করা যাক -


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
public class Main {
    public static void main(String[] args) {
        Directory root = new Directory("root");
        File file = new File("profile.jpg");
        root.add(file);

        Directory movie = new Directory("movie");
        root.add(movie);

        Directory englishMovie = new Directory("english");
        englishMovie.add(new File("IronFist.mp4"));
        englishMovie.add(new File("The Shawshank Redemption.mp4"));
        englishMovie.add(new File("ZotaPia.mp4"));
        File despicableMe = new File("DespicableMe.mp4");
        englishMovie.add(despicableMe);

        movie.add(englishMovie);

        Directory banglaMovie = new Directory("Bangla");
        banglaMovie.add(new File("The Clay Bird.mp4"));
        banglaMovie.add(new File("Jibon Thekey Neya.mp4"));
        movie.add(banglaMovie);

        root.printTree();

        System.out.println("name: " + movie.getName());
        System.out.println("Created: " + movie.getCreated());
    }
}

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


+--root/
|  +--movie/
|  |  +--Bangla/
|  |  |  +--Jibon Thekey Neya.mp4/
|  |  |  +--The Clay Bird.mp4/
|  |  +--english/
|  |  |  +--DespicableMe.mp4/
|  |  |  +--IronFist.mp4/
|  |  |  +--The Shawshank Redemption.mp4/
|  |  |  +--ZotaPia.mp4/
|  +--profile.jpg/

name: movie
Created: 2017-04-08T02:26:38.341


সম্পূর্ণ কোডটি পাওয়া যাবে এখানে: https://gist.github.com/rokon12/d3c83562c785de6d1a483a5585205b92