そらとぶへび

仕事・プライベートを通しての気づき、JavaやPHP、データベースやサーバの話などこつこつと書いていきます

初めてAmazon CESを触る前にざっくりと調べた概要メモ

AWS ECS(Amazon Elastic Container Service)とは、Docker コンテナのオーケストレーション

クラスタ

新規または既存のVPC内にAmazon ECS クラスタを構成する。
クラスタとは1つのかたまりとしてタスクを可動させるためのコンテナ(EC2)群。
設定画面では、EC2インスタンスタイプ、インスタンス数、VPC、サブネットなどを設定する。
コンテナは、Docker技術を使用しており、 インスタンスイメージはDockerfile から構築される。
docs.aws.amazon.com


タスク定義

アプリやバッチ処理をコンテナ上で実行するためのタスク定義を行う。
コンテナイメージのURL、CPU、メモリのハード/ソフト制限、環境変数の設定、
コンテナの開始時に実行するコマンドなど、様々なパラメータをJSON形式で設定可能。
具体的なパラメータは使用する起動タイプに応じて変化する。

docs.aws.amazon.com


docs.aws.amazon.com


サービス定義

タスクとクラスタを関連付ける。
設定する内容としては、サービス名とタスク数(起動するコンテナの数)。
また、ECSではサービスのコンテナ間のトラフィックを負荷分散するためのELBを設定することができる。

タスクのスケジューリング

ScheduleTaskをりよう
CloudWatchのイベントのルールやターゲットを使用して、cronのようなスケジュール実行の設定ができる。

その他(競合サービスなど)

他のクラウドサービスで同様な機能としては次のものがある。
・Azure Container Service
Google Container Engine(GKE)

ラムダ式その1

ラムダ式はメソッド定義を式として記述できるJava8以降の言語仕様。ラムダ式を使うことで、クラスやインタフェースより小さな粒度の振る舞いを抽象化することができる。

public class Main {

	@FunctionalInterface
	interface Appender{
		public void app(StringBuffer sb,String s);
	}
	
     public static void main(String[] args) {

    	 Appender appender = (sb,s) -> sb.append(s);

    	 StringBuffer result = new StringBuffer();
    	 appender.app(result, "aaa");
    	 appender.app(result, "bbb");
    	 
    	 System.out.println(result);
     }
}

ラムダ式の文法

(仮引数列)-> {処理本体の文(複数)}
または
(仮引数列)-> 処理本体の式

(int x,int y) -> {return x + y;}
または
(int x,int y) -> x + y

// 仮引数の型名は省略できる。すべてを省略するかすべてを省略しないのどちらか。
(x,y) -> x + y
// 仮引数が一つの場合は丸括弧を省略できる。
x -> x + 1
// 仮引数が一もない場合は場合は丸括弧は省略できない。
() -> 1
// 中括弧でくくった場合、returnする必要がある。
x -> {return x + 1;}
// 中括弧でくくった場合、複数行の記述が可能。
x -> {
	x + 1;
	return x;
	}

ラムダ式とローカル変数の関係

import java.util.function.Consumer;

public class Main {
	
	// Consumerは標準関数型インタフェースの一つ(入力があり、出力なし)
	Consumer<String> createProc(String paramValue){
		String localValue = "local";
		Consumer<String> proc = s-> System.out.println("s:"+ s + ",paramValue:" + paramValue +",localValue:" + localValue);
		return proc;
	}

     public static void main(String[] args) {

    	 Main ma1 = new Main();
    	 Consumer<String> proc = ma1.createProc("aaaa");
    	 
    	 // ラムダ式メソッドの実行
    	 proc.accept("bbbb");
     }
}

実行結果

s:bbbb,paramValue:aaaa,localValue:local

標準関数型インタフェース

java.utilfunctionパッケージで定義される関数型インタフェース。

Function R apply(T t) T型オブジェクトによる入力、R型オブジェクトの出力
Predicate boolean test(T t) T型オブジェクトによる入力、boolean型の出力
Consumer void accept(T t) T型オブジェクトによる入力、出力なし
Supplier T get() 入力なし、T型オブジェクトの出力

標準型インタフェースで初めの処理を書き直した例

import java.util.function.*;

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

    	 IntFunction<StringBuffer> create = StringBuffer::new;
    	 StringBuffer sb = create.apply(8);
    	 
    	 Function<String, StringBuffer> appender = sb::append;
    	 appender.apply("abc");
    	 
    	 BiConsumer<StringBuffer, String> appender2 = StringBuffer::append;
    	 appender2.accept(sb, "def");
    	 
    	 System.out.println(sb);
     }
}

実行結果

abcdef

インタフェースの設計

インタフェースとは、振る舞いを部品として抽象化するための言語仕様。クラスと違い実体化できずメソッドも実装を持たない。インタフェースの設計にあたっては、クラス間の依存を少なくするよう留意する。
以下に留意点をまとめる。

コードの依存性

他のクラスへの依存性が少ないほど、そのコードは堅牢ということになる。通常、クラスへの依存より、インタフェースへの依存のほうが保守に強いコードとなる。

・変化しにくい振る舞いを規定してインタフェースにまとめる。
・クラスはインタフェースを継承することで、変化しにくい部分を表明する。
・可能な限りクラスよりインタフェースに依存するように書く。


インタフェースと抽象クラス

実装上、インタフェースの役割は抽象クラスでも担える。ただし根本的な目的は異なっている。抽象クラスによる実装を伴う継承関係はクラス間に強い依存関係をもたらしてしまう。そのため、インタフェースは実装を一切持たず、振る舞いのみを規定する機能に限定されている。

インタフェース :振る舞いを規定する。
抽象クラス   :実装の拡張を行う。

インタフェース実装例

インタフェース依存によりクラス間の依存を減らすことができる。直接、メソッドを呼び出せばコードとしては単純になるが、クラス間の依存が強くなる。

MyFilter.java (インタフェース)

public interface MyFilter {
	String doJob(String input);
}

ReplaceFilter.java (インタフェースの実装クラス)

public class ReplaceFilter implements MyFilter {

	private final String oldStr;
	private final String newStr;

	
	public ReplaceFilter(String oldStr,String newStr){
		this.oldStr = oldStr;
		this.newStr = newStr;
	}
	@Override
	public String doJob(String input) {
		return input.replaceAll(oldStr, newStr);
	}
}

CapitalizeFilter.java (さらにインタフェースを実装したクラス)

public class CapitalizeFilter implements MyFilter {
	@Override
	public String doJob(String input) {
		return input.toUpperCase();
	}
}

MyEcho.java (実行クラス)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;

public class MyEcho {
	
	private final List<MyFilter> filters;

	public MyEcho(List<MyFilter> filters){
		this.filters = filters;
	}
	
	
	public void exec() {
		try(BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
			while(true) {
				System.out.println("Input any text");
				String msg = stdin.readLine();
				
				String output = msg;
				for(MyFilter filter : filters) {
					output = filter.doJob(output);
				}
				
				System.out.println("You sey " + output);
			}
		}catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String... args) throws ReflectiveOperationException{
		MyEcho echo = new MyEcho(Arrays.asList(new ReplaceFilter("he", "she"),new CapitalizeFilter()));
		echo.exec();
	}

}

テキストの整形処理部分を外部に追い出し、呼び出し方法をインタフェース化する。これにより整形処理に変更があっても実行クラスへの変更は最小限になる。

アノテーションによるコード呼び出し

アノテーションを定義する例と、リフレクションによるアノテーションのコード呼び出し例。
MyFilterというアノテーションを定義する。
実行時にリフレクションによる呼び出しを行うので、RetentionはRUNTIMEを指定する。

MyFilter.java  アノテーションを定義する。

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFilter {
	enum Potion{
		BEFORE,
		AFTER,
	}
	Potion value();
}


CapitalizeFilter.java 定義したアノテーションを適用する。

public class CapitalizeFilter {
	@MyFilter(MyFilter.Potion.BEFORE)
	public String doJob(String input) {
		return input.toUpperCase();
	}
}

次のコードでCapitalizeFilterクラスから、MyFilterアノテーションを適用したメソッドを呼び出しを行う。
isAnnotationPresentでアノテーションの有無を判定を行う。
getAnnotationでもアノテーションの参照を取得しているので、やや冗長かもしれない。

MyEcho.java

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

public class MyEcho {
	
	private static class Filter{
		Filter(Object obj, Method method){
			this.obj = obj;
			this.method = method;
		}
		final Object obj;
		final Method method;
	}
	
	
	private final List<Filter> filters = new ArrayList<>();

	public MyEcho(String... classNames) throws ReflectiveOperationException{
		// プラグインコードのロード
		
		for(String className : classNames) {
			Class clazz = Class.forName(className); // リフレクションを使用してクラス名を取得
			
			for(Method method : clazz.getMethods()) {
				System.out.println("MyFilter.class " + MyFilter.class);
				if(method.isAnnotationPresent(MyFilter.class)) {  // アノテーションの有無を判定
					System.out.println("method " + method);
					MyFilter myfilter = method.getAnnotation(MyFilter.class);
					if(myfilter != null) {
						switch(myfilter.value()) {
						case BEFORE:
							filters.add(0,new Filter(clazz.newInstance(),method));
							break;
						case AFTER:
							filters.add(new Filter(clazz.newInstance(),method));
						}
					}
				}
			}
		}
	}
	
	
	public void exec() {
		try(BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
			while(true) {
				System.out.println("Input any text");
				
				String msg = stdin.readLine();
				
				String output = msg;
				for(Filter filter : filters) {
					output = (String)filter.method.invoke(filter.obj, output);
				}
				
				System.out.println("You sey " + output);
			}
		}catch(IOException| ReflectiveOperationException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String... args) throws ReflectiveOperationException{
		MyEcho echo = new MyEcho(args);
		echo.exec();
	}

}

実行結果

java MyEcho CapitalizeFilter 
Input any text
abc
You sey ABC

アノテーションの基礎

アノテーションとは、プログラムの構成要素に付与するメタ情報で、修飾子の一つとして機能する。Java SE 5 から導入された機能であり、 @Xxxxxx の形式で、Java の言語機能で表現できない補足情報を注釈 (annotation) として記述するための仕組み。アノテーションコンパイラや解析ツール、もしくはフレームワークによって処理される。

アノテーション適用可能なプログラム要素

  • 型宣言
  • フィールド変数宣言
  • メソッド宣言
  • パラメータ変数宣言
  • コンストラクタ宣言
  • ローカル変数宣言
  • アノテーション型宣言
  • パッケージ宣言

アノテーションの役割

アノテーションの主な役割として「構文補助」と「宣言的プログラミング」がある。

  • 構文補助

 @Overrideなどが構文補助にあたり、活用することでJavaの文法の独自拡張に近いことができる。

  • 宣言的プログラミング

 プログラムを機能や役割で部品に分解し、形式的に記述する。ツールやフレームワークで解析することで、部品の間をつなぐコードを自動生成することができる。

標準アノテーション

Javaの標準ライブラリでで提供されているアノテーションのことを、標準アノテーションと呼ぶ。

アノテーション 説明
@Override   オーバライドしたメソッドであることを明示
@Deprecated  非推奨メソッドであることを宣言
@SuppressWarnings  警告メッセージを抑制

アノテーション型の宣言

アノテーションは型の一種であり、他の型同様に独自で作成することができる。
@interfaceが指定されているクラスは、暗黙的にjava.lang.annotation.Annotationインタフェースの継承クラスになる。

アノテーション型の宣言の文法

[修飾子] @interface Annotation名{
    キーの型 キーの名前(); // アノテーション要素
    ...
}

// アノテーション要素のある例
public @interface SuppressWarnings {
  String value();
}
// enumを利用したアノテーション要素のある例
public enum eType {AAA, BBB, CCC}
@interface EnumAnnotation {
  eType value();
}

//複数のアノテーション要素のある例
public @interface MyAnnotation {
  String foo();
  int bar();
  class cls();
  String[] qux();
}


//デフォルト値を指定した複数のアノテーション要素のある例
public @interface MyAnnotation {
  String foo() default "aaa";
  int bar();
  class cls();
  String[] qux();
}

アノテーション要素の型は次に限定される。

  • 基本型
  • String型
  • enum
  • アノテーション
  • Class型
  • 上記を要素とする配列型(配列の配列は不可)

アノテーション要素のないアノテーションをマーカーアノテーションと呼ぶ。(@Overrideなど)


また、アノテーションの宣言は、抽象メソッド宣言と比較して次の制約がある。

  • 引数は持てない。
  • throws節が持てない。
  • ジェネリックメソッドが使えない。

メタアノテーション

アノテーションを独自で作成する際に使用するアノテーションをメタアノテーションと呼ぶ。
あらかじめ用意されている標準メタアノテーション型は次のものがある。

アノテーション名  説明
@Target   アノテーションが、どの要素に利用できるものかを宣言する
@Retention  アノテーションの存在期間を宣言する
@Documented  アノテーションjavadocによる文書化の対象とする
@Ingerited   アノテーションが独自に継承されることを宣言する。
@Repeatable  同じ対象要素に同じアノテーションを複数指定可能(java8以降)
Target アノテーション

アノテーションがどの要素(クラスやメソッドなど)に対して適用することができるかを定義する。指定する要素は enum 型の java.lang.annotation.ElementType で定義される。


値 説明

TYPE クラス・インタフェース・enumアノテーションの宣言
FIELD フィールドの宣言(enum定数を含む)
METHOD メソッドの宣言
PARAMETER メソッドのパラメータの宣言
CONSTRUCTOR コンストラクタの宣言
LOCAL_VALIABLE ローカル変数の宣言
ANNOTATION_TYPE アノテーション型の宣言
PACKAGE パッケージの宣言
Retention アノテーション

アノテーションで付加された情報がどの段階まで保持されるかを定義する。指定する段階は enum 型の java.lang.annotation.RetentionPolicy で定義される。

説明
SOURCE ソース上だけのアノテーションで、クラスファイルには記録されない。
CLASS クラスファイルにもアノテーションの情報が記録される。実行時にはロードされない。Retention アノテーションを指定しなかった場合、CLASS がデフォルトで定義される。
RUNTIME クラスファイルに情報が記録され、さらに実行時にも情報がロードされる。JavaプログラムからリフレクションAPIを利用してアノテーションの情報を読み取るためには、この種別が必要。
Documented アノテーション

対象となるアノテーションにより付加した情報が javadoc コマンドなどで作成したドキュメントに反映されなければならないことを示す。Documented アノテーションはマーカーアノテーションであり、要素は持たない。どのようにドキュメントに反映されるかは、ドキュメントを生成するプログラムの実装に依存する。

Inherited アノテーション

クラス宣言に付加するアノテーションが、対象となるクラスのサブクラスにも継承されることを指示する。

アノテーションの適用

基本的には「アノテーション要素名 = アノテーション要素値」を記述する。
要素が配列の場合は中括弧{}で値を指定する。
なお、アノテーション要素名がvalueのときのみ「value=」は省略できる。

 @MyAnnotation(foo = "abc", bar = 1,cls = String.class, qux = {"aaa","bbb"})
 public String exec(String str){
   ・・・

コレクションと配列

配列はJavaの言語仕様で規定された機能であり、Javaの最初のバージョンから存在する機能。一方、コレクションはJavaのバージョン1.2から導入された標準ライブラリ。

  • コレクションフレームワークの種類
  • コレクション型オブジェクトの生成
  • リスト(java.util.List)
  • マップ(java.util.Map)
    • HashMap
    • LinkedHashMap
    • TreeMap
  • セット
  • スタック、キュー、デック

コレクションフレームワークの種類

(インタフェース)リスト
 具象クラス → ArrayList,LinkedList

(インタフェース)マップ
 具象クラス → HashMap,TreeMap,LinkedHashMap

(インタフェース)セット
 具象クラス → HashSet,TreeSet,LinkedHashSet

(インタフェース)デック
 具象クラス → ArrayDeque,LinkedDeque


コレクション型オブジェクトの生成


コレクションのインタフェース型<要素の型> 変数 = new コレクションのインタフェースを実装した具象クラス<>(コンストラクタの引数);

例)
List slist = new ArrayList<>();
//List slist = new ArrayList(); // Java7以前の書き方


要素の型に、基本型(char,intなど)は指摘できない。ラッパークラス(String,Integerなど)を使う。

続きを読む

自責思考と他責思考の活用

自責思考の持ち主は、問題を自分のものとして考え、解決方法を考えて行動する。自分の行動はすぐに変えられる。

他責思考の持ち主は、「問題の原因は自分の手に届く範囲にない」と考えてしまい、本人はなにも行動を起こさない。他責では問題が解決されない。

自責思考 自分の問題として考える
他責思考 問題を他者のせいにする


こう言ってしまうと、自責思考は優秀な人間で、他責思考をする人はダメな人間のようになるのか。手放しでそうとは言い切れない気がしている。
過剰な自責思考の持ち主は、自己満足や独りよがりで行動を起こして、より問題を複雑化する。すべての責任を背負い込んだ結果、柔軟な解決方法を見いだせず、大きなストレスを抱え込むことになる。

そもそも、責任が「誰」にあるのかを考えるのがナンセンスなんだろう。

「プロセスやモノの仕組みのせい」と考える。

いい意味での他責思考を上手に活用することで、改善策を発想し易くする事ができる。

プロセスやモノであれば、変えることができる。変えられるもの、自らの行動で変化させることができるものに焦点を絞る。別のものに置き換えてみる、組み合わせる、排除してみる、修正してみる。

自責思考も他責思考も良い面と悪い面がある。他責思考を悪く使うと、責任逃ればかりで成長機会を失う。自責思考を悪く使うと自己肯定感が下がる。他責思考を悪く使うと、責任逃ればかりで成長機会を失う。ただしどちらも良い面を活用することで、自己の成長につながる。

自責思考だけでなく、他責思考をうまく活用してしていくことが、働き方の改善や生産性の向上につながっていくのだと思う。