技術とか戦略とか

SIerで証券レガシーシステムを8年いじってからSESに転職した普通の文系SEによる技術ブログ。

JavaScript:オブジェクト型の定数値にはObject.freeze()を使う

プリミティブ型(Number型やString型等)の変数は、constで定義することで変更不可にできます。
これを利用して、定数値を定義することができます。
 
【サンプルコード】
const hoge = 1;
hoge = 2;
console.log(hoge);
 
ChromeデベロッパーツールのConsoleで実行】
Uncaught TypeError: Assignment to constant variable.
at <anonymous>:2:6
 
----
 
しかし、オブジェクト型の場合、constで保持されるのは変数が指し示すアドレスであり、値は変更可能です。
そのため、定数値を定義するには適していません。
 
【サンプルコード】
const obj = {
 hoge: 1
};
obj.hoge = 2;
console.log(obj.hoge);
 
ChromeデベロッパーツールのConsoleで実行】
2
 
----
 
オブジェクト型で定数値を定義したい場合は、Object.freeze()を使う必要があります。
これを使うことで、オブジェクト型の変数が指し示す値を変更不可にすることができます。
 
【サンプルコード】
const obj = {
 hoge: 1
};
Object.freeze(obj);
obj.hoge = 2;
console.log(obj.hoge);
 
ChromeデベロッパーツールのConsoleで実行】
1

Vue.js:componentでpropsを使って属性を受け取る

Vue.jsでは、componentを使用して独自タグを定義することができます。
これは部品化に役立ちます。
 
独自タグではslotタグにより値を受け取ることができますが、propsプロパティを用いて属性値を受け取ることもできます。
属性値はテンプレート構文で使用できるため、dataプロパティ等と組み合わせて柔軟なDOM操作が可能になるメリットがあります。
また、今回は紹介しませんが、propsプロパティでは必須チェックやデフォルト値設定やバリデーションも実装できるので、それらの機能を実装してより便利に使うこともできます。
 
以下、サンプルコードです。
propsプロパティを用いることで、テンプレート構文での使用が可能になっていること({{ }}で囲った箇所への埋め込みが可能になっていること)に注目してください。
 
【サンプルコード】
・sample1.html(propsプロパティを使わない例)
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - sample1</title>
</head>
<body>

<h1>タイトル表示テスト</h1>
<div id="app">
 <my-component>hoge</my-component>
 <my-component>fuga</my-component>
 <my-component>piyo</my-component>
</div>
<h5>※これらのタイトルはVue.jsで出力しています</h5>

<script src="https://unpkg.com/vue"></script>
<script>
Vue.component('my-component', {
 // バッククォートで囲ってHTMLを直接記述。slotの箇所に値埋め込み。
 template: `<h3>タイトル:<slot></slot></h3>`
});

// Vueオブジェクトの生成
var app = new Vue({
 el: "#app"
});
</script>
</body>
</html>
 
・sample2.html(propsプロパティを使う例)
<!DOCTYPE html>
<html lang="ja">
<head>
 <meta charset="UTF-8">
 <title>Vue.js - sample2</title>
</head>
<body>

<h1>タイトル表示テスト</h1>
<div id="app">
 <my-component title="hoge"></my-component>
 <my-component title="fuga"></my-component>
 <my-component title="piyo"></my-component>
</div>
<h5>※これらのタイトルはVue.jsで出力しています</h5>

<script src="https://unpkg.com/vue"></script>
<script>
Vue.component('my-component', {
 // シングルクォートで囲ってテンプレート構文を記述。属性値を受け取る。
 data: function () {
  return {
   titleHeader: "タイトル:"
  }
 },
 props: ['title'],
 template: '<h3>{{ titleHeader }}{{ title }}</h3>'
});

// Vueオブジェクトの生成
var app = new Vue({
 el: "#app"
});
</script>
</body>
</html>
 
【実行結果】
sample1.html、sample2.html共に、以下のように表示されます。

f:id:akira2kun:20210530164146j:plain

Chromeでharファイルを出力する手順

ChromeデベロッパーツールのNetworkタブでは、アクセスしたページとやりとりされたリクエストやレスポンスについて、その内容や所要時間を確認することができます。
そして、Networkタブで確認した内容は、harファイルと呼ばれるファイルに出力し、保存することができます。
 
harファイルを出力する手順は以下の通りです。
 
【手順】
1.Chromeを立ち上げる。
2.F12でデベロッパーツールを立ち上げる。
(「その他ツール」→「デベロッパーツール」、Ctrl+Shift+Iでも可)
3.「Network」タブをクリックする。

f:id:akira2kun:20210529224126j:plain

4.録画状態なら停止する(画像中③が赤丸ならクリックして黒丸にする)
5.既に出力されているリクエスト・レスポンスを削除する(画像中①をクリック)
6.「Preserve log」にチェックを入れる(画像中②のチェックを入れる)
7.録画状態にする(画像中③をクリックして赤丸にする)

f:id:akira2kun:20210529224138j:plain

8.ブラウザ上で任意の操作を行う(リクエスト・レスポンスが記録される)
9.画像黄枠内で右クリックし、"Save all as HAR with content"をクリック

f:id:akira2kun:20210529224151j:plain

10.ファイル保存ウインドウが開くので、任意のフォルダにharファイルを保存
 
----
 
ちなみに、harファイルはjson形式で保存されています。
そのままでは人間の目で確認するのが困難ですが、"HTTP Archive Viewer(http://www.softwareishard.com/har/viewer/)"のようなページでharファイルを解釈することで、ChromeのNetworkタブのような表示形式に加工することができます。

 

f:id:akira2kun:20210529224213j:plain

 

ChromeからJavaScriptを実行する

Chromeデベロッパーツールの機能の一つであるConsoleでは、任意のJavaScriptを実行することができます。
コマンドプロンプトDOSコマンドを実行するような感覚でJavaScriptを実行することができるので、JavaScriptの仕様のちょっとした確認を行いたい場合に便利です。
 
【手順】
1.Chromeを立ち上げる。
2.F12でデベロッパーツールを立ち上げる。
(「その他ツール」→「デベロッパーツール」、Ctrl+Shift+Iでも可)
3.「Console」タブをクリックする。
4.任意のJavaScriptを記述し、実行する。
(Shift+Enterで改行、Enterで実行)

f:id:akira2kun:20210529182900j:plain



 
----
 
また、Consoleで実行するJavaScriptにより、現在開いているページに影響を与えることもできます。
例えば、MDNのJavaScriptチュートリアルのトップページ(https://developer.mozilla.org/ja/docs/Web/JavaScript)にアクセスし、下記コードを実行すると、ページの文字列置換('JavaScript'を'[JavaScript]'に置換)を行うことができます。
 
document.body.innerHTML=document.body.innerHTML.replace(/JavaScript/g,'[JavaScript]');

 

f:id:akira2kun:20210529182915j:plain

 

ソースコード中に顧客情報を記述してはいけない

特定の顧客用に特別なビジネスロジックを用意している場合、以下のようにソースコード中に顧客情報を記述することで実装することはできます。
(顧客番号が'1111111'である場合に特別なビジネスロジックを実行するとします)
 



if (処理中の顧客番号が'1111111'の場合) {
  特別なビジネスロジック
}



 
----
 
しかし、この実装方法には以下の問題があり、望ましくありません。
特別なビジネスロジックを適用する対象の顧客の情報は、(データの管理にDBを使用しているシステムであれば)DBに持たせるべきです。
 
・対象となる顧客が追加されたり削除されたりする度にソースコード修正が必要になる
 コンパイルやデプロイが必要となり、対応工数や対応のリスクが増加する。
 →DBで管理すればDB内の情報の補正だけで済む。
 
・データを一元管理できなくなる
 対象となる顧客を調べるためにはソースコードを見なくてはならなくなる。
 →DBで一元管理すれば情報の検索が容易になる。
 
・セキュリティを確保するのが難しくなる
 ソースコードが流出した場合に顧客情報も同時に流出してしまう。
 (参考:フリマアプリ「メルカリ」で2021/05/21に発表された情報流出事故
 →DBで一元管理すれば、顧客情報の流出のリスクを減らすことができる。
 
----
 
対象となる顧客の情報をDBに持たせる場合、以下のような実装になります。
 
1.対象となる顧客の情報を管理するテーブルを別途持たせる場合

f:id:akira2kun:20210523182640j:plain




if (特定処理対象顧客テーブルに処理中の顧客番号が存在する場合) {
  特別なビジネスロジック
}



 
2.既存のテーブルにフラグを持たせる場合

f:id:akira2kun:20210523182702j:plain



if (処理中の顧客番号について顧客テーブル.特定処理対象フラグが'1'の場合) {
  特別なビジネスロジック
}


java:ソートキーが複数存在する場合、一時的なフォーマット変更ではなく独自Comparatorで対応するべき

複数のソートキーが存在するオブジェクトの配列について、固定長のフォーマットに直すことでソートキーを1つにできます。
しかし、フォーマットを直して1つのキーでソートできるようにするよりも、独自Comparatorを定義して複数のキーでソートした方が高速なので、そのようなケースでは独自Comparatorを定義して対応するべきです。
 
単純にフォーマット変更でコストがかかるだけでなく、ソート処理自体も複数のキーでソートした方が高速です。
複数のキーを1つのキーにまとめてソートする場合は必ずキー全体を見る必要がありますが、複数のキーでソートする場合はキーの一部を見るだけでソートできる場合があるので、複数のキーでソートした方が高速になるのだと思います。
 
以下、テスト結果です。
 

ソースコード
・SortTest.java
import java.util.ArrayList;
import java.util.Collections;

public class SortTest {

  public static void main(String[] args) {

    // ソート対象のArrayListの作成
    // 一意な10,000,000個のデータを作成
    // それをランダムにソートする
    ArrayList<SortTestRecord> array1 = new ArrayList<>();
    for (int i = 0; i < 1000000; i++) {
      array1.add(new SortTestRecord( (i / 100000) % 10,
                     (i / 10000) % 10,
                     (i / 1) % 10000) );
    }
    Collections.shuffle(array1);
    ArrayList<SortTestRecord> array2 = new ArrayList<>(array1);

    // 3つのソートキーによりソートする場合
    long startTime1 = System.currentTimeMillis();
    SortTestComparator comparator = new SortTestComparator();
    Collections.sort(array1, comparator);
    long endTime1 = System.currentTimeMillis();

    // フォーマット変更して1つのソートキーでソートする場合
    long startTime2 = System.currentTimeMillis();
    ArrayList<String> array2tmp = new ArrayList<>();
    for (int i = 0; i < array2.size(); i++) {
      array2tmp.add(String.format("%02d", array2.get(i).getSortkey1()) +
             String.format("%03d", array2.get(i).getSortkey2()) +
             String.format("%05d", array2.get(i).getSortkey3()));
    }
    long startTime2inside = System.currentTimeMillis();
    Collections.sort(array2tmp);
    long endTime2inside = System.currentTimeMillis();
    array2 = new ArrayList<>();
    for (int i = 0; i < array2tmp.size(); i++) {
      array2.add(new SortTestRecord(
          Integer.parseInt(array2tmp.get(i).substring(0,2)),
          Integer.parseInt(array2tmp.get(i).substring(2,5)),
          Integer.parseInt(array2tmp.get(i).substring(5,10))));
    }
    long endTime2 = System.currentTimeMillis();

    // 結果確認
    System.out.println("[サンプル出力]");
    System.out.println("(1つ目、123,457つ目、1,000,000つ目を出力)");
    array1.get(0).print();
    array1.get(123456).print();
    array1.get(999999).print();
    System.out.println();
    System.out.println("[配列の差異箇所出力]");
    int diffCount = 0;
    for (int i = 0; i < 1000000; i++) {
      if (array1.get(i).getSortkey1() != array2.get(i).getSortkey1() ||
        array1.get(i).getSortkey2() != array2.get(i).getSortkey2() ||
        array1.get(i).getSortkey3() != array2.get(i).getSortkey3()) {
        System.out.println( (i + 1) + "つ目の要素の内容を出力");
        array1.get(i).print();
        array2.get(i).print();
        diffCount++;
      }
    }
    if (diffCount == 0) {
      System.out.println("差異無し。");
    }
    System.out.println();
    System.out.println("[処理時間]");
    System.out.println("・3つのソートキーによりソートを実施");
    System.out.println( (endTime1 - startTime1) + " ms");
    System.out.println("・フォーマット変更を行いソートを実施");
    System.out.println( (endTime2 - startTime2) + " ms");
    System.out.println(" (内、ソート部分だけの時間)");
    System.out.println(" " + (endTime2inside - startTime2inside) + " ms");

  }

}
 
・SortTestRecord.java
public class SortTestRecord {

  private int sortkey1 = 0;
  private int sortkey2 = 0;
  private int sortkey3 = 0;

  // コンストラク
  SortTestRecord(int sortkey1, int sortkey2, int sortkey3) {
    this.sortkey1 = sortkey1;
    this.sortkey2 = sortkey2;
    this.sortkey3 = sortkey3;
  }

  // getter
  public int getSortkey1() {
    return this.sortkey1;
  }

  // getter
  public int getSortkey2() {
    return this.sortkey2;
  }

  // getter
  public int getSortkey3() {
    return this.sortkey3;
  }

  // プリント用クラス
  public void print() {
    System.out.println(sortkey1 + "," + sortkey2 + "," + sortkey3);
  }

}
 
・SortTestComparator.java
import java.util.Comparator;

public class SortTestComparator implements Comparator<SortTestRecord> {

  @Override
  public int compare(SortTestRecord o1, SortTestRecord o2) {
    if (o1.getSortkey1() < o2.getSortkey1()){
      return -1;
    } else if (o1.getSortkey1() > o2.getSortkey1()){
      return 1;
    } else if (o1.getSortkey2() < o2.getSortkey2()){
      return -1;
    } else if (o1.getSortkey2() > o2.getSortkey2()){
      return 1;
    } else {
      return o1.getSortkey3() - o2.getSortkey3();
    }
  }

}

 
【結果(1回目)】
[サンプル出力]
(1つ目、123,457つ目、1,000,000つ目を出力)
0,0,0
1,2,3456
9,9,9999

[配列の差異箇所出力]
差異無し。

[処理時間]
・3つのソートキーによりソートを実施
2750 ms
・フォーマット変更を行いソートを実施
19105 ms
 (内、ソート部分だけの時間)
 3827 ms
 
【結果(2回目)】
[サンプル出力]
(1つ目、123,457つ目、1,000,000つ目を出力)
0,0,0
1,2,3456
9,9,9999

[配列の差異箇所出力]
差異無し。

[処理時間]
・3つのソートキーによりソートを実施
1692 ms
・フォーマット変更を行いソートを実施
13627 ms
 (内、ソート部分だけの時間)
 3350 ms
 
【結果(3回目)】
[サンプル出力]
(1つ目、123,457つ目、1,000,000つ目を出力)
0,0,0
1,2,3456
9,9,9999

[配列の差異箇所出力]
差異無し。

[処理時間]
・3つのソートキーによりソートを実施
2177 ms
・フォーマット変更を行いソートを実施
15333 ms
 (内、ソート部分だけの時間)
 3224 ms
 
【結果まとめ】

f:id:akira2kun:20210522193143j:plain

 

Visual Studio Codeでdraw.ioを使う

フローチャートやネットワーク図等は、フリーの作図ツール「draw.io」を使うと楽に記述できます。
Excelでも記述はできますが、draw.ioでは各々の図の記述に適した素材が提供されているという特徴があります。
また、draw.ioはブラウザ上での使用が想定されていますが、フリーのコードエディタである「Visual Studio Code」を用いるとローカル上で編集が可能になります。
 
今回は、使用手順を簡単に紹介していきたいと思います。
 
1.draw.ioのホームページにアクセスする。
https://app.diagrams.net/
 
2.ファイルの保存方法を選択する。
今回は「Device」を選択します。これで図がローカルに保存できるようになります。
また、選択後、新規にファイルを作成するかどうか聞かれるので、新規にファイルを作成する(Create New Diagram)を選択します。

f:id:akira2kun:20210520025533j:plain
 
3.どのような図を作成するか選択する。
今回は「Flowchart」を選択します。

f:id:akira2kun:20210520025940j:plain
 
4.ブラウザ上でファイルを編集してみる。
例えば、図形を追加する場合は、「Shapes -> General」から追加することができます。
また、図形の中に文字を入力したい場合は「右クリック -> Edit」で入力できます。
図を保存したい場合は、画面上部の「Click here to save.」から保存できます。

f:id:akira2kun:20210520025901j:plain

f:id:akira2kun:20210520025916j:plain
 
5.Visual Studio Code拡張機能を入れる。
ローカルでVisual Studio Codeを開き、拡張機能の検索画面を開きます。
そして、「draw.io」を検索し、「Draw.io Integration」をインストールします。

f:id:akira2kun:20210520030006j:plain
 
6.4でローカルに保存したファイルをVisual Studio Codeで開く。
すると、以下のように、ローカルでdraw.ioの図を編集できるようになります。
図形の追加は「General」から、文字編集は図形をダブルクリックすることで可能です。

f:id:akira2kun:20210520030025j:plain