技術とか戦略とか

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

singletonとstaticの違い

デザインパターンのsingletonパターンを学んだ時に「インスタンスを1個にすれば良いなら、クラス変数やメソッドをstaticにすれば良いのでは?」と思ったので、試しにテストコードを書いてみました。
単純なstaticでは利用者側で好きにインスタンスを作成できる(実態としては1つ)のですが、singletonパターンなら利用者側でのインスタンス作成を防ぎ、明示的にインスタンスを1つにできることを確認しました。
 
【テストコード】
・StaticMemory.java
public class StaticMemory {

 private static int num = 0;

 public static void setNum(int arg) {
  num = arg;
 }

 public static int getNum() {
  return num;
 }

}
 
・SingletonMemory.java
public class SingletonMemory {

 private static int num = 0;
    private static SingletonMemory instance = new SingletonMemory();

    private SingletonMemory(){
        // インスタンスを生成
    }

    public static SingletonMemory getInstance(){
        return instance;
    }

 public static void setNum(int arg) {
  num = arg;
 }

 public static int getNum() {
  return num;
 }

}
 
・MemoryTestMain.java
public class MemoryTestMain {

 // 要件:最新の値をメモリに保持したい
 @SuppressWarnings("static-access")
 public static void main(String[] args) {

  int recentNum = 0; // 最新の値

  // 普通のStaticなクラスを使った場合
  recentNum = 1;
  StaticMemory.setNum(recentNum);

  // 使う側がオブジェクトを作れてしまう
  StaticMemory instance1 = new StaticMemory();
  recentNum = 2;
  instance1.setNum(recentNum);

  StaticMemory instance2 = new StaticMemory();
  recentNum = 3;
  instance2.setNum(recentNum);

  // Staticなので実際には何れのオブジェクトも同じアドレスを指す
  System.out.println("元のStaticクラス:"+StaticMemory.getNum());
  System.out.println("作ったオブジェクト1:"+instance1.getNum());
  System.out.println("作ったオブジェクト2:"+instance2.getNum());

  // Singletonなクラスを使った場合
  // オブジェクトがprivateなので外部から作れない(下記はエラー)
  // SingletonMemory instance3 = new SingletonMemory();

  // 唯一のオブジェクトを取得する必要がある
  recentNum = 4;
  SingletonMemory.getInstance().setNum(recentNum);
  System.out.println
  ("Singletonクラス:"+SingletonMemory.getInstance().getNum());

 }

}
 
【実行結果】
元のStaticクラス:3
作ったオブジェクト1:3
作ったオブジェクト2:3
Singletonクラス:4
 
-------------------------
 
"Singleton"を"Singletone"とtypoしている箇所があったので直しました。

「不可視です」というエラーが出たらカプセル化された変数・メソッドを使用しようとしていないか疑う

タイトルの通りなのですが。
 
例えば、
「int hoge = new java.util.Random().next(10)」
というコードを書いた場合、
「next(int)はRandomで不可視です」
というエラーが出ます。
 
Randomクラスのnextメソッドはprotectedで定義されており、外部に提供されていないメソッドであるため、外部から使おうとするとエラーになります。
外部に提供されているメソッドとしてはnextIntメソッド(publicで定義)があるので、
「int hoge = new java.util.Random().nextInt(10)」
とすればコンパイルエラーは消えます。

基本情報処理技術者試験のpythonのサンプル問題を解いてみた

 

2019年10月28日にIPAから基本情報処理技術者試験のpythonのサンプル問題が公表されたので、解いてみました。
既に基本情報技術者試験.comから解説も出ています。
 
【2019年10月28日公開】基本情報技術者試験 Pythonサンプル問題|基本情報技術者試験.com

https://www.fe-siken.com/kakomon/sample/python_sample.html

 
----------------------
 
解いてみた感想を一言で言うと、
pythonの知識がなくても、頑張ってトレースすれば解ける」
でした。
シラバスの内容を照らし合わせて考えてみても、pythonの細かい文法知識を問われているというよりは、pythonを使って各種ライブラリを使いこなせるかどうかを問われていると思って良いでしょう。
 
サンプル問題を全問正解するには、以下に示す前提知識が必要になります。
高校の文系数学の知識はないと厳しいですが、pythonの知識はそこまで深く問われているわけではありません。
疑似言語を解けるだけの技量があれば、pythonの知識不足で点を落とす可能性があるのは9問中1問(設問2のc)だけだと思います。
言語選択の問題で、自分が得意な言語の問題がたまたま苦手な問題であった場合、pythonに逃げるのを検討するのも一つの合格テクニックではないかなと思います。
 
【数学の知識】
・0度の場合はx軸を右側に進む
・一回転は360度である
・三角比の知識
f:id:akira2kun:20191110195822j:plain
 
pythonの知識】
・文字列のスライシング
 例えば、x[2:5]とすれば3文字目から5文字目を取得できる。
 始点と終点は省略できる。
 (人によっては、知らなくても選択肢から察しがつくかもしれない)
・stackの使い方
 (ただし、設問中のロジックを見れば、知らなくても十分に察しがつく)
 
----------------------
 
情報処理技術者試験に関する記事の目次

https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

unix/linux:atコマンドの代替コマンド

特定の日時にコマンドが発行されるように登録するatコマンドですが、環境によってはこのコマンドが使用できないことがあります。
特定のユーザでしか実行できないように制御することができたり、OSによってはatコマンドで登録できない仕様(バグ?)があったりするためです。
 
この場合、以下のようなループ処理をワンライナーかつバックグラウンドで使用することで、atコマンドと同じようなことができるようになります。
 
・20:00に/home/hoge/test.txt(空ファイル)を作成するように登録する例
#> (TIME=`date "+%H%M"; while[${TIME} -lt 2000]; do sleep 60; TIME=`date "+%H%M"; done; touch /home/hoge/test.txt) &

unix/linux:コマンドをファイルの代わりにする(プロセス置換)

Linuxのコマンドの中にはファイル名を指定するものがあります。
ここで、ファイル名の代わりに、コマンドをファイルに見立てて指定することができます。
(プロセス置換と呼びます)
 
例えば、決められた日時にコマンドが実行されるように登録するatコマンドがあるのですが、このatコマンドでワンライナーをしたい場合に役に立ちます。
以下のようにすることで、対話式での手入力やファイル作成をすることなく、1行のコマンドでatコマンドのコマンド登録を行うことができます。
 
・20:00に/home/hoge/test.txt(空ファイル)を作成するように登録する例
#> at 20:00 -f <(touch /home/hoge/test.txt)

java:イミュータブルなクラスを作る方法

「イミュータブル」とは「不変」という意味で、オブジェクト指向の世界では「状態(クラス変数)がオブジェクト生成時から変更されないこと」を指します。
有名所では、Stringがイミュータブルなクラスとして知られています。
 
イミュータブルなクラスを自作するためには、以下の条件を満たすようにクラスを作成する必要があります。
 
①クラス変数はprivateで定義する
 クラス変数が外のクラスから直接書き換えられるのを防ぐため
 
②setterメソッドを定義しない
 クラス変数を書き換えるメソッドは定義しない
 
③クラスをfinalで定義する
 サブクラスでクラス変数を書き換えられるのを防ぐため
 
④ミュータブルなオブジェクトへの参照を含んでいない
 ミュータブルなオブジェクトを変更する処理を記述しないようにするため
 
クラスをイミュータブルにするメリットとしては、内部の状態の変化を気にする必要が無くなるために処理を追いやすくなるというのがあります。
関数型プログラミングにも通じる考え方なのですが、オブジェクト生成より後で内部の状態が書き変わることがないため、クラスのメソッドが返す値が一定となり、内部の状態の変化を気にしながらトレースする必要がなくなります。
また、
hoge2 = hoge1;
のようなオブジェクトのアドレスのコピーが使いやすくなります。
イミュータブルではないクラスでこのコピー方法を用いると、hoge2の状態の変化がhoge1にも影響してしまい(hoge1とhoge2で同じアドレスを指しているため)、思わぬ影響が出ることがあります。
しかし、イミュータブルなクラスであれば、状態を変化させようと思えば新たにオブジェクトを作り直して新たなメモリ領域を確保しなければならないので、hoge2の状態を変化させても(オブジェクトを作り直しても)hoge1へ影響するのを防ぐことができます。
イミュータブルではないクラスでも、オブジェクトの中身全体をコピーすれば思わぬ影響を防ぐことはできるのですが、オブジェクトのアドレスをコピーする場合と比べてメモリ使用量が増え処理も遅くなります。
 
以下で、イミュータブルなクラスを定義してそれを使用するサンプルコードを示します。
 
【サンプルコード】
・ImmutableCalc.java
// ③クラスをfinalで定義する
public final class ImmutableCalc {

    // ④ミュータブルなオブジェクトへの参照を含んでいない

    // ①クラス変数はprivateで定義する
    private int num = 0;

    // オブジェクト生成時のみ状態が変化する
    public ImmutableCalc(int num) {
        this.num = num;
    }

    // ②setterメソッドを定義しない
    public int singleCalc() {
        return num;
    }

    public int doubleCalc() {
        return num * 2;
    }

    public int tripleCalc() {
        return num * 3;
    }

}
 
・ImmutableTestMain.java
public class ImmutableTestMain {

    public static void main(String[] args) {

        // イミュータブルなクラスのオブジェクトを作成
        ImmutableCalc hoge1 = new ImmutableCalc(1);

        // オブジェクトのアドレスをコピー
        // オブジェクトの中身をコピーするよりメモリ使用量が減り処理も速い
        ImmutableCalc hoge2 = hoge1;

        // 値を書き換えるにはnewして新たにメモリ領域を確保するしかない
        hoge2 = new ImmutableCalc(2);

        // hoge2の変更がhoge1に影響を与えない
        // アドレスをコピーするテクニックを安心して使える
        System.out.println("hoge1:"+hoge1.singleCalc());
        System.out.println("hoge2:"+hoge2.singleCalc());

        // オブジェクト生成時にしか値が書き変わらないので処理を追いやすい
        System.out.println("hoge1_double:"+hoge1.doubleCalc());
        System.out.println("hoge1_triple:"+hoge1.tripleCalc());

    }

}
 
【実行結果】
hoge1:1
hoge2:2
hoge1_double:2
hoge1_triple:3

マニュアル対応にならないようにするために心がけるべきこと

サービス提供者、特に接客業では、「マニュアル対応」が批判されることがあります。
「マニュアル対応」が批判される理由としては、以下の二点の理由により顧客の期待通りのサービスを行えない可能性があるからだと思っています。
 
・マニュアルはサービス提供者側の見えない都合を顧客に押し付けるものである
・マニュアルは性悪説の観点で労働者を縛り臨機応変な対応を妨げるものである
 
-----------------------
 
ここで、マニュアル対応が批判された例として、「おにぎりを買ってコンビニで割り箸を要求したら断られた」という例を挙げていきたいと思います。
「あらかじめ買っておいたカップ麺を食べたいが、箸を忘れてしまったので、コンビニでおにぎりを買って割り箸をもらおうとしたら、おにぎりには割り箸はつけられないと言われてくれなかった」という批判であり、具体的には以下のような批判となっています。
 
コンビニでお箸を要求したら断られました  生活・身近な話題  発言小町  読売新聞

https://komachi.yomiuri.co.jp/t/2013/0904/615599.htm

 
これについては、批判する側の気持ちは正直わかります。
 
コンビニでは何に割り箸をつけて何に割り箸をつけないのかが不明瞭です(サービス提供者側の見えない都合)。
そのため、弁当やカップ麺に割り箸がついてきたら、「何か食べ物を買ったら割り箸をつけてくれる」と顧客が誤解する可能性があります。
個人差はあるものの、弁当やカップ麺以外でも割り箸を使うケースは考えられます。
googleで「おにぎり 箸で食べる」で検索すると箸でおにぎりを食べる人がいることがわかりますし、「汚れた手でキーボードに触りたくない」という理由でスナック菓子を箸で食べながら作業する人も見たことがあります。
そのため、「おにぎりやスナック菓子でも言えば割り箸をくれる」と顧客が誤解してもおかしなことではありません。
このような誤解が発生すると、実際に提供できるサービス以上のサービスを顧客側が期待することに繋がります。
 
また、労働者側から見れば、マニュアルにより「おにぎりに割り箸はつけられません」の一点張りするしかなくなってしまうというのも問題です(臨機応変な対応を妨げる)。
最低限、顧客の要望に応えられていないことに気付いて本心から謝罪するべきなのですが、マニュアルがあると労働者は「マニュアル通りに働けば良い」という意識になりがちなため、その気付きすら妨げます。
仮に気付けたとしても、サービス提供者側の見えない都合を説明した上で「1回だけサービスで割り箸をつける(数円ながらサービス提供者側にダメージは出る)」「割り箸を1本5円か10円で売る(今回のケースでは顧客が本当に欲しいのは割り箸なので、渋々払ってくれる可能性が高い)」といった臨機応変な対応をすることができず、マニュアル通りの対応に終始するしかありません。
 
その結果、顧客から見れば「期待したサービスを受けられなかった」という思いが残り、顧客満足度が下がったりクレームにつながったりします。
 
接客業には本当にどうしようもないクレームもあるにはあるのですが、このケースについてはサービス提供者側ができることがまだまだ残されているケースだと思います。
見えない都合があるならそれを可視化するべきです。弁当やカップ麺を割り箸と一緒に梱包すれば何に割り箸がついてくるのか一目瞭然ですし、それがコストや陳列スペース的に難しいのであればシールを貼って割り箸がついてくる商品を可視化する手もあります。
また、いくら可視化に勤めたとしても、誤解から過剰なサービスを顧客が期待してしまうことを完全に防ぐことはできないので、労働者側が臨機応変な対応を行えるようにする必要もあります。
ただし、臨機応変な対応を行うためには、労働者側が自分のサービスを理解した上で顧客の要望を汲み取る必要があるので、労働者側に前向きな姿勢が求められます。労働者を管理する側も、労働者を信頼した上で、必要な教育を適宜行う必要があります。
 
-----------------------
 
私も、今の現場では社内の開発者向けのサービス(ソースコードを規約に沿った形に変換)に関わっているのですが、「サービス提供者側の都合を可視化する」「顧客の要望を汲み取り臨機応変な対応を行う」という2点は忘れないようにしています。
具体的には、サービスに過大な期待を持たれないように依頼書のフォーマットを工夫したり適宜必要な情報展開を行ったりする、「変換されたソースコードが読みにくい」「変換後ソースを開発者が再修正しなければならなくなるケースがある」といったことがあればその場は臨機応変な対応で切り抜けた上で変換ツール修正で改善する、といったことを行っています。
少なくとも、期待通りのサービスを提供できなかった時は申し訳ないという気持ちは持っています。
 
サービスを提供するからには、少しでも要望に応えられるように努力していきたいです。