Understand the Composite pattern by comparing JavaScript and Java code

Introduction

Details and other patterns will be written in ** Understanding design patterns by comparing implementations in JavaScript and Java **. I wrote an example of JavaScript by looking at Java. We do not take advantage of differences in features such as class type / prototype type, typed strength, and access modifiers. Please note.

Composite pattern

There is a "directory" in your computer's file system There are other directories and files in the directory Directories create such "nested" structures, recursive structures

qiita1.PNG qiita2.PNG

Treat directories and files together as directory entries It may be convenient to treat the container and contents as the same type

A pattern that equates the container with the contents and creates a recursive structure is called the Composite pattern. composite means "mixture" "complex"

Implementation example in Java

A program that schematically represents files and directories

Class diagram

Composite.png

code

Main.java


public class Main {
    public static void main(String[] args) {
        try {
            System.out.println("Making root entries...");
            Directory rootdir = new Directory("root");
            Directory bindir = new Directory("bin");
            Directory tmpdir = new Directory("tmp");
            Directory usrdir = new Directory("usr");
            rootdir.add(bindir);
            rootdir.add(tmpdir);
            rootdir.add(usrdir);
            bindir.add(new File("vi", 10000));
            bindir.add(new File("latex", 20000));
            rootdir.printList();

            System.out.println("");
            System.out.println("Making user entries...");
            Directory yuki = new Directory("yuki");
            Directory hanako = new Directory("hanako");
            Directory tomura = new Directory("tomura");
            usrdir.add(yuki);
            usrdir.add(hanako);
            usrdir.add(tomura);
            yuki.add(new File("diary.html", 100));
            yuki.add(new File("Composite.java", 200));
            hanako.add(new File("memo.tex", 300));
            tomura.add(new File("game.doc", 400));
            tomura.add(new File("junk.mail", 500));
            rootdir.printList();
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }
}

Entry.java


public abstract class Entry {
    public abstract String getName();
    public abstract int getSize();
    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException();
    }
    public void printList() {
        printList("");
    }
    protected abstract void printList(String prefix);
    public String toString() {
        return getName() + " (" + getSize() + ")";
    }
}

Directory.java


import java.util.Iterator;
import java.util.ArrayList;

public class Directory extends Entry {
    private String name;
    private ArrayList<Entry> directory = new ArrayList<Entry>();
    
    public Directory(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public int getSize() {
        int size = 0;
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            size += entry.getSize();
        }
        return size;
    }
    public Entry add(Entry entry) {
        directory.add(entry);
        return this;
    }
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
        Iterator it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = (Entry)it.next();
            entry.printList(prefix + "/" + name);
        }
    }
}

File.java


public class File extends Entry {
    private String name;
    private int size;
    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }
    public String getName() {
        return name;
    }
    public int getSize() {
        return size;
    }
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
    }
}

FileTreatmentException.java


public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException() {

    }
    public FileTreatmentException(String msg) {
        super(msg);
    }
}

Implementation example in JavaScript

code

index.html


<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Composite</title>
</head>
<body>
    <script src="Main.js"></script>
    <script src="Directory.js"></script>
    <script src="File.js"></script>
</body>
</html>

Main.js


MAIN = {};

MAIN.init = function() {
    console.log("Making root entries...");
    MAIN.rootdir = new Directory("root");
    MAIN.bindir = new Directory("bin");
    MAIN.tmpdir = new Directory("tmp");
    MAIN.usrdir = new Directory("usr");
    MAIN.rootdir.add(MAIN.bindir);
    MAIN.rootdir.add(MAIN.tmpdir);
    MAIN.rootdir.add(MAIN.usrdir);
    MAIN.bindir.add(new File("vi", 10000));
    MAIN.bindir.add(new File("latex", 20000));
    MAIN.rootdir.printList();

    console.log("");
    console.log("Making user entries...");
    MAIN.yuki = new Directory("yuki");
    MAIN.hanako = new Directory("hanako");
    MAIN.tomura = new Directory("tomura");
    MAIN.usrdir.add(MAIN.yuki);
    MAIN.usrdir.add(MAIN.hanako);
    MAIN.usrdir.add(MAIN.tomura);
    MAIN.yuki.add(new File("diary.html", 100));
    MAIN.yuki.add(new File("Composite.java", 200));
    MAIN.hanako.add(new File("memo.tex", 300));
    MAIN.tomura.add(new File("game.doc", 400));
    MAIN.tomura.add(new File("junk.mail", 500));
    MAIN.rootdir.printList();
};

window.addEventListener("load", MAIN.init);

Directory.js


var Directory = function(name) {
    this.name = name;
    this.directory = [];
};

Directory.prototype = {
    constructor: Directory,

    getName: function() {
        return this.name;
    },
    getSize: function() {
        var size = 0;
        for (var i = 0, max = this.directory.length; i < max; i++) {
            size += this.directory[i].getSize();
        }
        return size;
    },
    add(entry) {
        this.directory.push(entry);
        return this;
    },
    printList: function(prefix) {
        if (arguments.length === 0) {
            console.log("/" + this.getName() + " (" + this.getSize() + ")");
            for (var i = 0, max = this.directory.length; i < max; i++) {
                this.directory[i].printList(this.getName());
            }
        } else {
            console.log(prefix + "/" + this.getName() + " (" + this.getSize() + ")");
            for (var i = 0, max = this.directory.length; i < max; i++) {
                this.directory[i].printList(prefix + "/" + this.getName());
            }
        }
    }
}

File.js


var File = function(name, size) {
    this.name = name;
    this.size = size;
};

File.prototype = {
    constructor: File,

    getName: function() {
        return this.name;
    },
    getSize: function() {
        return this.size;
    },

    printList(prefix) {
        console.log(prefix + "/" + this.getName() + " (" + this.getSize() + ")");
    }
};

Characters in the Composite pattern

** The role of Leaf **

A role that represents "contents" You can't put anything else in this role Sample program ⇒ File (class)

** The role of Composite **

The role of "container" You can put a Leaf role or a Composite role Sample program ⇒ Directory (class)

** Role of Component **

A role to equate the Leaf role with the Composite role The Component role is expressed as a superclass common to the Leaf role and Composite role. Sample program ⇒ Entry (abstract class)

** The role of Client **

Composite pattern users Sample program ⇒ Main (class)

Composite pattern class diagram

test.png

Need for Composite pattern

Sample.java


// Directory
public int getSize() {
  int size = 0;
  Iterator it = directory.iterator();
  while (it.hasNext()) {
    Entry entry = (Entry)it.next();
    size += entry.getSize();
  }
  return size;
}

// File
public int getSize() {
  return size;
}

In either case, you can get the size with the same method getSize. This is a manifestation of the Composite pattern's characteristic, "considering the container and contents as the same thing."

You can recursively call the tree structure to get the size

qiita3.PNG

Data structures that are generally tree-structured fit the Composite pattern

When using the Composite pattern

Composite pattern etc. are used in the test program

You can use the Composite pattern when you want to use these three as input tests.

Related patterns

reference

[Introduction to Design Patterns Learned in the Augmented and Revised Java Language](https://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82% E7% 89% 88Java% E8% A8% 80% E8% AA% 9E% E3% 81% A7% E5% AD% A6% E3% 81% B6% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3% E5% 85% A5% E9% 96% 80-% E7% B5 % 90% E5% 9F% 8E-% E6% B5% A9 / dp / 4797327030)

Recommended Posts

Understand the Composite pattern by comparing JavaScript and Java code
Understand the Strategy pattern by comparing JavaScript and Java code
Understand the Decorator pattern by comparing JavaScript and Java code
Understand the State pattern by comparing JavaScript and Java code
Understand design patterns by comparing implementations in JavaScript and Java [Updated from time to time]
Try to implement and understand the segment tree step by step (python)
Java compilation and execution understood by CLI
Read the file by specifying the character code.
Understand the Decision Tree and classify documents
Learn the design pattern "Composite" in Python