技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

java:Enumによりコード値に意味を持たせ可読性を向上させる

javaEnum(列挙型)を使用するメリットとしては、一般的に「使用可能な定数を明確化できる」「定数を複数のクラスで使い回せる」といったメリットが挙げられます。
実際に使用していて、コード値に意味を持たせられるというメリットもありそうだったので、記事に残します。
 
コーディングをしていると、コード値を用いた方が便利な場合が間々あります。
例えば、RPGの道具に対して、「weakSwordにはID0、normalSwordにはID1、strongSwordにはID2を割り振る」といった具合です。
コード値を用いれば、コード値を用いて配列操作することが可能になりますし、DBでマスタ管理する際にも便利です。
 
しかし、コード値には「人間が理解し辛い」というデメリットがあります。
コンピュータで処理する分には効率が良いのですが、人間から見たら「IDが0ならweakSword、IDが1なら…」といった具合で、コード値が何を意味しているのかを予め知る必要が出てきてしまいます。
コード値とその意味の対応付けを人間が行うことで、読み間違い・書き間違いといったバグに繋がるミスも起こりやすくなります。
 
そこで、Enumでコード値とその意味の対応付けを行えば、「ソースコード上は意味のある単語、コンピュータ上はコード値」という形にできます。
そうすることで、コード値とその意味の対応付けを人間が行う必要が無くなり、読み間違い・書き間違いを未然に防ぐことができます。
 
以下、サンプルコードです。
 
【サンプルコード】
・ItemEnum.java
public enum ItemEnum {
    weakSword(0),
    normalSword(1),
    strongSword(2);

    private int id;
    public final static int ITEM_TOTAL_NUMBER = 3; // 道具の総数

    private ItemEnum (int id) {
      this.id = id;
    }

    public int getId() {
        return id;
    }

    public static ItemEnum valueOf(int id) {
        ItemEnum[ ] array = values();
        for(ItemEnum num : array) {
            if (id == num.getId()){
                return num;
            }
        }
        return null;
    }

    public static int getItemPrice(ItemEnum itemEnum) {

        int[] result = new int[ITEM_TOTAL_NUMBER];

        result[weakSword.getId()] = 100;
        result[normalSword.getId()] = 200;
        result[strongSword.getId()] = 300;

        return result[itemEnum.getId()];

    }

}
 
・ItemManagementMain.java
public class ItemManagementMain {

    public static void main(String[ ] args) {

        // 道具袋を定義し、道具を入れていく
        // javaの言語仕様でint型は初期値0のため初期化は不要
        int[ ] itemBag = new int[ItemEnum.ITEM_TOTAL_NUMBER];
        itemBag[ItemEnum.weakSword.getId()]++;
        itemBag[ItemEnum.normalSword.getId()]++;
        itemBag[ItemEnum.strongSword.getId()]++;
        itemBag[ItemEnum.weakSword.getId()]++;
        itemBag[ItemEnum.strongSword.getId()]++;

        // 道具袋の中の数を確認
        System.out.println("・道具袋の中身の確認");
        for(int i = 0; i < ItemEnum.ITEM_TOTAL_NUMBER; i++) {
            System.out.println
                (" " + ItemEnum.valueOf(i) + " は " + itemBag[i] + " 個 ");
        }

        // 道具の値段を確認
        System.out.println();
        System.out.println("・道具の値段の確認");
        for(int i = 0; i < ItemEnum.ITEM_TOTAL_NUMBER; i++) {
            System.out.println
                (" " + ItemEnum.valueOf(i) + " の値段は " +
                    ItemEnum.getItemPrice(ItemEnum.valueOf(i)));
        }

    }

}
 
【実行結果】
・道具袋の中身の確認
 weakSword は 2 個
 normalSword は 1 個
 strongSword は 2 個

・道具の値段の確認
 weakSword の値段は 100
 normalSword の値段は 200
 strongSword の値段は 300

 

プライバシーマーク取得企業で働く上での心がけ(一般社員向け)(2019年度版)

私が勤める会社でもプライバシーマークを取得しており、2019年度版の一般社員向けの研修があったので、内容を簡単にまとめます。
研修テキストはネットに上げられませんし上げてもあまり意味はないので、一般論的なことを簡単にまとめるだけにします。
詳細なことに関しては各企業毎で話されると思いますし、ルールは各企業毎で微妙に違うはずなので。
 
ちなみに、情報処理技術者試験でも「プライバシーマーク制度とは何か」といった設問が出ることがあり、関連する個人情報保護法等も出題されることがあるので、情報処理技術者試験対策の上でも知っておくべきことの一つです。
 
プライバシーマーク制度とは
 JIPDEC(一般財団法人日本情報経済社会推進協会)が運用している制度である。
 
 日本工業規格「JIS Q 15001」をベースに、
 「個人情報保護法
 「各省庁が作成した個人情報保護法に関するガイドライン
 「地方自治体による個人情報関連の条例」
 といった関連法規も考慮した独自の認定基準が設けられている。
 個人情報の管理体制を整えている企業にプライバシーマークが認定・付与され、
 会社の信用度を外に示すためにプライバシーマークを使用することを許される。
 
 「プライバシーマーク」と同じようなプライバシー関連の第三者認証としては、
 「TRUSTe」「ISMS」といったものが存在する。
 
■JIS Q 15001の要求事項
 ・個人情報保護方針(プライバシーポリシー)を公開していること
 ・個人情報保護マネージメントシステム(PMS)を確立していること
 ・実施のための手順が確立していること
 
■個人情報とは
 特定の個人を特定できる情報である。
 氏名や住所はもちろんのこと、社員番号や本人の画像等も含まれる。
 
■個人情報の安全管理措置
 個人情報について「漏洩」「滅失」「毀損」
 を防ぐための安全管理措置を取ることが重要である。
 社内に対しては、個人情報管理のルールを定め、必要な投資を行うのが有効。
 社外に対しては、取引先の個人情報取り扱いの体制を確認した上で、
 契約書による管理を行うのが有効。
 
個人情報保護法とは
 個人情報の不適切な取り扱いを未然に防止するための法律(予防法)である。
 内容は年々更新されているので、最新の情報を確認することが重要である。
 (情報処理技術者試験対策という意味では、最新のテキストを読むのが重要)
 
 2017~2018年の改定では以下の点がポイントになる。
 ・事業者の監督権限を一元的に有する個人情報保護委員会の新設
 ・個人の身体的特徴(顔認証データ、指紋認証データ等)も個人情報に追加
 ・要配慮個人情報(人種、信条、病歴等)の定義
 ・5000人以下の個人情報を取り扱う事業者にも個人情報保護法を適用
 ・利用目的の変更条件の緩和
 ・匿名加工情報(個人を識別できないように加工した個人情報)の定義
 ・オプトアウト手続き(本人の同意を得ない第三者提供)の厳格化
  (個人情報保護委員会への届出を義務化)
 ・個人情報の提供時のトレーサビリティの確保
 ・個人情報が不要になった際に消去するように努力義務を明記
 ・個人情報の不正な流通が発覚した際の個人情報保護委員会による調査の明記
 ・個人情報保護法違反に対する立ち入り調査と指導、助言、勧告、命令の明記
 ・個人情報データベース等不正提供罪の新設
 ・外国への個人情報提供の取り扱いの定義の追加
 ・消費者の意見収集や事業者への指導等を行う認定個人情報保護団体の活用
 ・裁判における個人情報の開示請求権を定義
 
マイナンバーとは
 国民一人一人を一意に特定するために、12桁の番号が与えられるものである。
 (法人向けは13桁)
 2019年12月現在は、社会保障、税金、災害対策のみに使用され、
 公正公平な社会の実現、国民の利便性向上、行政効率化を図ることができる。
 マイナンバーの取り扱いについてはマイナンバー法により定められており、
 本人が同意しても利用目的外の利用や第三者への提供ができないなど、
 個人情報保護法よりも厳しく定められている。
 
■個人情報が漏洩した際のリスク
 個人情報が漏洩したことによる被害を補償する必要が出てくる他、
 会社に対する信頼が失墜し、その後の事業活動にも悪影響が出る。
 
■個人情報を取り扱う上で一般社員が注意するべきこと
 会社毎で取り扱いのルールを決めているはずなので、
 上位者の指示にしたがってそのルールを守ることが重要となる。
 (おかしいと思うことがあれば上位者に相談することも重要)
 
 個人情報取り扱いの事故の原因として、2019年12月現在では、
 「メール誤送信」が増加傾向にあるが、
 「紛失」や「宛名間違い等」は減少傾向にある。
 
 一般的には、以下のような事項についてルールが定められる。
  ・個人情報の取得や取り扱いのルール
   (本人の承認を取る、クリアデスク等)
  ・PCのスクリーンセーバー設定
  ・メール送信時の宛先・CC・BCCの使い分け
  ・メールの添付ファイルの送り方
   (パスワード付きzipにしてパスワードは別途送付等)
  ・メーラーの設定変更
   (メール送信前にメール内容を確認できる設定にする等)
  ・ノートPCのハードディスク暗号化やワイヤロック
  ・私的端末の使用の制限

javaでのAbstract Factoryパターン

Abstract FactoryパターンはFactory Methodパターンを発展させたもので、生成するオブジェクトの組み合わせを間違えないために、1つのFactoryクラスに複数のオブジェクトの生成処理を実装するようにしたものです。
 
今回は、RPGのキャラクター作成を模したサンプルコードを作成してみました。
キャラクターの職業毎に、職業を示すオブジェクトとスキルを示すオブジェクトを生成します。
両オブジェクトの組み合わせ方を間違えないように、Abstract Factoryパターンにより組み合わせ方を限定しています。
 
【サンプルコード】
・AbstractJob.java
public abstract class AbstractJob {

    protected String playerName;
    protected String jobName;

    public abstract void setPlayerName(String playerName);
    public abstract String getPlayerName();
    public abstract void setJobName(String jobName);
    public abstract String getJobName();

}
 
・WarriorJob.java
public class WarriorJob extends AbstractJob {

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    public String getPlayerName() {
        return this.playerName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobName() {
        return this.jobName;
    }

}
 
・MagicianJob.java
public class MagicianJob extends AbstractJob {

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    public String getPlayerName() {
        return this.playerName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobName() {
        return this.jobName;
    }

}
 
・AbstractSkill.java
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractSkill {

    protected String playerName;
    protected List<String> skillList = new ArrayList<String>();

    public abstract void setPlayerName(String playerName);
    public abstract String getPlayerName();
    public abstract void setSkill(String skill);
    public abstract List<String> getSkill();

}
 
・WarriorSkill.java
import java.util.List;

public class WarriorSkill extends AbstractSkill {

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    public String getPlayerName() {
        return this.playerName;
    }

    public void setSkill(String skill) {
        this.skillList.add(skill);
    }

    public List<String> getSkill() {
        return this.skillList;
    }

}
 
・MagicianSkill.java
import java.util.List;

public class MagicianSkill extends AbstractSkill {

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    public String getPlayerName() {
        return this.playerName;
    }

    public void setSkill(String skill) {
        this.skillList.add(skill);
    }

    public List<String> getSkill() {
        return this.skillList;
    }

}

 
・AbstractJobFactory.java
/*
 * 生成するオブジェクトの組み合わせを間違えないように、
 * 複数のオブジェクトの生成を1クラスにまとめる
 */
public abstract class AbstractFactory {

    public final AbstractJob createJob(String playerName) {
        AbstractJob abstractJob = createJobLogic(playerName);
        return abstractJob;
    }

    protected abstract AbstractJob createJobLogic(String playerName);

    public final AbstractSkill createSkill(String playerName) {
        AbstractSkill abstractSkill = createSkillLogic(playerName);
        return abstractSkill;
    }

    protected abstract AbstractSkill createSkillLogic(String playerName);

}
 
・WarriorFactory.java
/*
 * WarriorJobとWarriorSkillの組み合わせ
 */
public class WarriorFactory extends AbstractFactory {

    protected AbstractJob createJobLogic(String playerName) {
        WarriorJob warriorJob = new WarriorJob();
        warriorJob.setPlayerName(playerName);
        warriorJob.setJobName("Warrior");
        return warriorJob;
    }

    protected AbstractSkill createSkillLogic(String playerName) {
        WarriorSkill warriorSkill = new WarriorSkill();
        warriorSkill.setPlayerName(playerName);
        warriorSkill.setSkill("QuickSlash");
        return warriorSkill;
    }

}
 
・MagicianFactory.java
/*
 * MagicianJobとMagicianSkillの組み合わせ
 */
public class MagicianFactory extends AbstractFactory {

    protected AbstractJob createJobLogic(String playerName) {
        MagicianJob magicianJob = new MagicianJob();
        magicianJob.setPlayerName(playerName);
        magicianJob.setJobName("Magician");
        return magicianJob;
    }

    protected AbstractSkill createSkillLogic(String playerName) {
        MagicianSkill magicianSkill = new MagicianSkill();
        magicianSkill.setPlayerName(playerName);
        magicianSkill.setSkill("FireL1");
        return magicianSkill;
    }

}

 
・CreateJobMain.java
import java.util.Iterator;

public class CreateJobMain {
    public static void main(String[] args){

        // Factoryメソッドの定義
        WarriorFactory warriorFactory = new WarriorFactory();
        MagicianFactory magicianFactory = new MagicianFactory();

        // Factoryメソッドによりオブジェクトを生成
        AbstractJob player1Job = warriorFactory.createJob("Player1");
        AbstractSkill player1Skill = warriorFactory.createSkill("Player1");
        AbstractJob player2Job = magicianFactory.createJob("Player2");
        AbstractSkill player2Skill = magicianFactory.createSkill("Player1");

        // 生成されたオブジェクトの内容を確認
        displayJob(player1Job,player1Skill);
        displayJob(player2Job,player2Skill);

    }

    private static void displayJob(AbstractJob job,AbstractSkill skill) {
        System.out.println("[プレイヤー:"+job.getPlayerName()+"]");
        System.out.println("・職業");
        System.out.println(" "+job.getJobName());
        System.out.println("・スキル");
        Iterator<String> iterator = skill.getSkill().iterator();
        if (!iterator.hasNext()) {
            System.out.println(" None");
        } else {
            while (iterator.hasNext()) {
                String str = iterator.next();
                System.out.println(" "+str);
            }
        }
    }

}
 
【実行結果】
[プレイヤー:Player1]
・職業
 Warrior
・スキル
 QuickSlash
[プレイヤー:Player2]
・職業
 Magician
・スキル
 FireL1

エクスプローラ上でフォルダを任意の順番で並べる(レジストリ変更無し・フリーソフト未使用)

エクスプローラ上でフォルダを任意の順番で並べたい場合、多くの場合は「00_…」「01_…」のような形で番号を割り振り、名前で並び替えると思います。
しかし、フォルダに番号を振らずに作業を進め、既に数多くのリンクが貼られてしまったような状況では、番号を振り直すのが難しくなります。
レジストリ変更やフリーソフトで対処することもできるそうなのですが、現場のポリシー次第では、セキュリティ等の問題でレジストリを自由に変更したりフリーソフトを自由に入れたりできないこともあります。
 
この場合、更新日時を任意の順番にし、更新日時で並び替えるようにすると良いです。
更新日時を任意の順番にするには、Windows PowerShell を使って自動的で更新日時を更新できるようにすると良いです。
(更新日時が更新されてしまった場合は Windows PowerShellスクリプトを実行することで更新日時を元に戻す必要は出てきてしまいますが、ワンクリックで済むのでそこまで面倒ではないと思います)
 
例えば、以下のようなフォルダ構成になっていて、「hoge」→「fuga」→「piyo」の名前に並び替えたい場合
f:id:akira2kun:20200103163545j:plain

以下のようなバッチを用意して実行します。
 
・FolderSort.bat
powershell -ExecutionPolicy RemoteSigned -File .\FolderSort.ps1
 
・FolderSort.ps1
Set-ItemProperty .\hoge -Name LastWriteTime -Value "2019/12/24 00:00:00"
Set-ItemProperty .\fuga -Name LastWriteTime -Value "2019/12/24 00:01:00"
Set-ItemProperty .\piyo -Name LastWriteTime -Value "2019/12/24 00:02:00"
 
「FolderSort.bat」をダブルクリックして実行すると更新日時が「hoge」→「fuga」→「piyo」の順番になるように更新され、更新日時で並び替えることで「hoge」→「fuga」→「piyo」の順番になります。

f:id:akira2kun:20200103163557j:plain

チェックリストの形骸化とその対策

バグの埋め込みを防ぐために、現場毎でチェックリストが設けられていることが多いです。
そのチェックリストには過去のバグの経験則が書かれていることも多く、ある意味宝物のようなものです。
 
しかし、実際の開発では、そのチェックリストが形骸化することも多いです。
前職ではチェックリスト改善に携わっていたこともあるのですが、私の経験上、以下の2つの理由で形骸化します。
 
・意味のないチェックをしている
 チェックリストの内容を正しく理解していないため、意味のないチェックがされる。
 
・チェックしたフリをしている
 無意味なチェックが多いため、真面目にチェックすると時間がかかりすぎる。
 そのため、慣れた人だと1つ1つ細かくチェックしなくなる。
 
これらの理由でチェックリストが形骸化した場合、チェックリストによるチェックが有効に機能せず、一度埋め込まれたことがあるバグが再び埋め込まれるということが度々起こります。
 
これを防ぐためには、チェックリストの定期的な整備が有効になります。
具体的には以下のような作業が有効です。
 
・チェック項目を調査する
 チェック項目がどのような意図で設けられたものなのか調査する。
 補記の必要があれば、チェックリストの文言を変えるなり別紙を作るなりする。
 チェックリストの内容を研修等で展開するのも有効。
 そのことにより、チェックリストの理解度が高まり、
 意味のあるチェックがされるようになる。
 また、不要と判断できるチェック項目があれば削除する。
 そのことにより、慣れた人がチェックを省略する動機が減る。
 
・自動チェックのツールを作成する
 ツールを作ることにより、チェックにかかる時間を短縮できる。
 そのことにより、慣れた人がチェックを省略する動機が減る。

人員追加は必ずしも悪とは限らない

IT業界では、「人員追加」という計画変更の方法はとにかく評判が悪いです。
Webで検索すると、「炎上プロジェクトで人員を追加したらますます燃え上がった」という話をあちこちで目にしますし、炎上プロジェクトへの対応案としてはまるでアンチパターンかのような扱われ方をすることが少なくありません。
 
かくいう私も「人員追加=アンチパターン」という認識だったのですが、会社の同僚とプロジェクトマネジメントの話をしている中で、認識が変わりました。
今では、「場合によっては人員追加は合理的な対応策」という認識に至っています。
この「場合によっては」というのは、具体的に言うと「炎上する未来が見えたタイミング」を指しています。
 
人員追加がダメな理由は、追加した人員を教育するコストがかかるからです。
元々のスキルが不足している場合はもとより、そうでない場合もプロジェクト固有の開発方針や業務知識を勉強してもらうのに時間がかかります。
一旦教育が終われば生産性は向上しますが、目の前の火を消さなければならないような短期決戦では有効な方法ではありません。
しかし、開発スコープ拡大や開発難易度の見誤り等で将来の炎上を予見できたタイミングであれば、短期的な教育コストを後々の生産性向上で取り戻せる可能性が出てきます。
そのような場合に、人員追加という方法は有効になります。
 
私の経験上も、設計の初期の段階で開発スコープの見誤りに気付き早急に人員追加をかけたプロジェクトが、最終的には成功プロジェクト(決められた期間内に元々の要件をクリアし、お客様からの評判も上々)となったのを目にしています。
私の経験と照らし合わせても、「場合によっては人員追加は合理的な対応策」という認識は正しいと思います。

net use によるネットワークドライブ割り当てで「このコマンドの構文は~」と言われる→ダブルクォーテーションで囲っていないのが原因かも

同じエラーで手が止まっている方向けに記事を起こします。
 
#> net use :z \\hoge\fuga(important)\piyo
 
のようにしてZドライブにネットワーク割り当てを行おうと思った所、「このコマンドの構文は次のとおりです:」という要は「使い方間違ってるよ!」というエラーが出てしまいました。
どうやら、パスの中に含まれていた括弧がメタ文字と判断されてしまい、ネットワーク上のパスだと判断されなかったみたいです。
 
以下のように、ダブルクォーテーションで囲ってメタ文字ではないことを明示すると通ります。
 
#> net use :z "\\hoge\fuga(important)\piyo"