うまとま君の技術めも

2015年新卒入社した社畜の勉強内容などなど

アジャイル検定 Lv.2 - 試験レポート

アジャイル検定 Lv.2 を受験してきたので、結果を含めて簡単なレポートを残しておきたいと思います。

アジャイル検定

アジャイル検定とは

アジャイル開発のスキルを客観的な尺度で分析・判定するのが、アジャイルソフトウエア開発技術者検定試験です。

試験システムとして、CBT(Computer Based Testing)を採用しています。
いつでも、どこでも受験することができます。4肢択一スタイルの問題、平均で70%の正解率を得られるよう、難易度を調整しています。
合格基準は80%以上の正解率です。

agilecert.org

アジャイル検定の構成

  • Lv.1試験
    • アジャイル開発に参加するのに必須となる知識が出題される
  • Lv.2試験
    • アジャイル開発のチームメンバーとして必須となる知識が出題される

アジャイル検定 Lv2.試験

試験範囲

試験時間および出題形式

  • 試験時間:60分
  • 出題形式:多肢選択式 (四肢択一) 40問
  • 評価方法:65%以上合格

Lv2.試験結果

f:id:umatomakun:20200130201555p:plain

試験勉強

umatomakun.hatenablog.com

感想

  • Lv2ではLv1に比べ圧倒的に複数選択可能な形式の問題が多かったので、より正確に理解していることが求められる
  • 各要素の目的を意識して勉強すると良さそう
  • 合格したものの、合格ラインギリギリだったので引き続き理解を深めるよう努めていきたいと思う

石井食品の攻守両輪:デザインスプリントとカイゼン - イベントメモ

DevLOVE

devlove.doorkeeper.jp

石井食品

  • 会社
  • 石井さん
    • 代表
    • 元エンジニア、CSM
  • 池田さん

守:見える化/ふりかえり/バックログ化/期待マネジメントでカイゼンした話

  • 部署
    • 課題解決チーム
      • 直販チーム
      • 通販チーム
  • 現場の悩み
    • 1.周りのメンバーは自分のことをどう思っている?
    • 2.仕事量が多いプレイングマネージャーはどうする?
    • 3.会議が多いのはどうすれば?
  • 対策
  • 実践(最初の一歩)
  • 不安を共有する

攻:ISHII SPRINT

  • 新商品開発をデザインスプリントする
    • デザインスプリント
      • Googleが提唱
      • 5日で課題解決
      • 理解→発散→決定→施策→検証
      • 課題解決に集中させるのが目的
      • 5日という制約があることで、無駄を省きつつ、アウトプットの質向上を狙う
  • なぜここにいるのか
    • 食品の新商品を作りたい
    • 新商品開発の型を作りたい
  • デジタルと食品の差
    • 問題解決という目的はどちらも同じ
    • 食品ならではの前提はある
  • ISHII SPRINT
    • 3日で行った
    • 総勢20人弱の規模で3チーム形成
    • Day1
      • ニーズを満たす(LeanCanvas)→どうやって検証するか?
    • Day2
      • 施策→フィードバック
    • Day3
      • 検証→お店で試食会
  • 学び
    • 短期集中で結果かが出ることの強さ
    • 部署間の壁を越えられる
      • 「良いものを作る」という1点のみに集中できる
    • クリティカルメイキング
  • ともに考え・ともに作る
    • 役割・部署を越えた協働

パネルディスカッション

  • 社風
    • 社長が子供を連れて会議にも参加
  • デザインスプリント
    • 池田さん→乾燥野菜のチーム
      • 1分で料亭の味
      • 部署の壁が心配だった
  • 年末催事大作成
    • 半分が新メンバー
    • 工場の方にもヘルプで入ってもらう
      • 目的を前もって共有しておけばよかった...
    • 人の取り合いになっていた...
  • きっかけ
    • 社長がOSTを開催→新商品開発の案が出た

ISHII SHOKUHIN 365

365.ishiifood.co.jp

感想

  • ソフトウェア開発に限らず、チームでゴールを目指して協力し合うことが大切だと改めて実感
    • 部署・役割の壁を取っ払うことで、より良いアイデアが生まれる
  • 企業のビジョンやミッションも重要になってくる印象
    • ビジョンやミッションに基づいた課題解決をするためにその会社で仕事するんだよね?
    • 会社・部署・チームそれぞれの単位で同じ目的を持つことで高みを目指したい
  • ただただ作業のようなプロダクト開発を行う組織は絶対に作りたくないなと思う
    • チームで同じ目的を持って課題解決目指す仕事のほうが楽しいよね

アジャイル検定Lv.2 勉強メモ - リファクタリング

アジャイル検定Lv.2 出題範囲

リファクタリング

リファクタリング

コードの不吉な臭い

  • 奇妙な名前
    • メソッドやクラスの名前が奇妙な場合
      • 不適切な命名は理解するのが難しい
      • 適切な名前が思いつかない場合は、設計が良くない兆候
  • 重複したコード
    • 同じコードが複数箇所にある場合
      • 重複コードがあると、複数箇所に同じ修正を行う必要がある
      • 重複コードを1箇所にまとめる
  • 長すぎるメソッド
    • メソッド名が長い場合
      • 短いメソッド名の方が理解しやすい
      • 短く役割が明確なメソッドを書く
  • 長すぎるパラメータリスト
    • メソッドの引数が多い場合
      • パラメータが多いと、1つ1つが何を意味しているのか理解しづらくなる
      • 新たなデータが必要になった時にパラメータリストの変更が必要となる
  • グローバルデータ
  • 変更可能なデータ
    • クラス内部のデータを変更している場合
      • データを変更すると予期しないバグを生み出す
      • データを書き換えるのではなく、変更後の新しいデータを持ったオブジェクトを返す
  • 変更の偏り
    • 1つのクラスが異なる理由で何度も変更されるような場合
      • 異なる役割にも変更の影響が出てしまう
      • クラスの役割が多すぎるので、役割を明確にし変更理由が1つになるようにする
  • 変更の分散
    • 1つの理由で複数のクラスが変更されるような場合
      • 変更すべき箇所が分散すると、重要な変更を実装し忘れる場合が出てしまう
  • 機能の横恋慕
    • クラスのメソッドが、別クラスのデータやメソッドとばかり処理を行っている場合
      • 役割の所在が異なっているため、メソッドを別クラスに移動させる
  • データの群れ
    • 同じ様なデータのグループが、複数箇所で使われている場合
      • 同じ役割であるため、1つのクラスにデータをまとめる
  • 基本データ型への執着
    • 基本データ型ばかりを使っている場合
      • 基本データ型をクラスに置き換えることで保守性を上げる
  • 繰り返されるSwitch文
    • 同じ様なSwitch文が何度も出てくる場合
      • 分岐を追加した場合に、すべてのSwitch文を変更する必要がある
      • Switch文の代わりにポリモーフィズムを使う
  • ループ
  • 怠け者
    • 十分な仕事をせず、理解したり保守するコストに見合わない場合
      • 不要なクラスは削除する
  • 疑わしき一般化
    • 将来必要になるという理由で書いたが、実際には使われず無駄になっている場合
      • 無駄に複雑になっているため、よりシンプルな方に変更する
  • 一時的属性
    • 特定の状況でしか使われないインスタンス変数がある場合
      • コードを理解しづらくなる
      • 別のクラスに切り出す
  • メッセージの連鎖
    • 複数のクラスを経由するメソッドチェーンがある場合
      • チェーンの途中で呼び出されるクラスに強く依存してしまう
      • 委譲を利用してチェーンを隠蔽する
  • 仲介人
    • クラス内のメソッドのほとんどが委譲を行っている場合
      • 仲介人を除去して、委譲先のオブジェクトと直接処理を行う
  • インサイダー取引
    • クラス間で過度にデータの交換を行っている場合
      • クラス間の結合が強くなってしまう
      • クラス間で共通して使用するデータを持つクラスを用意する
  • 巨大なクラス
    • 1つのクラスで多くの処理を行っている場合
      • クラスに役割が多すぎる
      • インスタンス変数を整理したり、親クラスに処理をまとめたりする
  • クラスのインターフェース不一致
    • 置き換えたいクラスのインターフェースが異なっている場合
      • 他のクラスへ置き換え可能にするには、インターフェースが同じである必要がある
      • インターフェースが同じになるようにする
  • データクラス
    • 属性・getter・setterしか持たないクラスがある場合
      • 必要な処理が誤った場所に書かれている可能性が高い
      • データクラスを使用しているコードを、データクラス自身に持たせられないか検討する
  • 相続拒否
    • 子クラスが親クラスの一部しか利用していない場合
      • 継承階層が間違っている
      • メソッド・変数の階層を変更したり、継承を移譲に変えたりする
  • コメント
    • 詳細なコメントが書かれている場合
      • コードの分かりづらさを補うためにコメントが記述されている
      • コメントを書かなくても内容が分かるようにする

参考資料

アジャイル検定Lv.2 勉強メモ - チームのスキル

アジャイル検定Lv.2 出題範囲

チームのスキル

スプリント計画

  • スプリントの作業を計画する
  • スプリント計画では以下の質問に答える
    • スプリントの成果物として何を作るか?
    • スプリントの成果物を作るために必要な作業をどのように成し遂げるか?

レトロスペクティブ

  • 次のスプリントの改善計画を作成する
    • 人・関係・プロセス・ツールの観点から振り返る
    • 成功したこと・失敗したこと・改善点を整理する
    • チームの作業を改善するための実施計画を作る

自己組織化されたチーム

  • 自己組織化
    • 自己組織化はチームが形成され、ある問題と制約が与えられたら、どのようにその問題を解決するかをチームが自分たちで決めるということ
  • 継続的改善
    • 自己組織化されたチームは最初のプロセスを忠実に守り、一定期間後に振り返りを行う
    • 失敗したこと・成功したこと・改善点が何かを考え、その結果に従ってプロセスを変えていく
  • 自己組織化されたチーム
    • 監督役を必要としない。外からタスクが割り当てられないし、進捗もチェックされない
    • プロジェクト・プロダクトに関する全ての情報を知ろうとする
    • 仕事に誇りを持ち、責任を引き受ける
    • 目的を与えられ、どのように目的に向かっているかを見られる
    • チームのルールを決め、そのルールに従う
    • 様々なタスクを解決し、メンバーの全ての専門性を活用する
    • 進捗を監視し管理する
  • 自己組織化されたチームを作るには

参考資料

アジャイル検定Lv.2 勉強メモ - デザインパターン

アジャイル検定Lv.2 出題範囲

デザインパターン

デザインパターン

  • よく出会う問題とそれにうまく対処するためのソフトウェア設計をパターン化し、再利用しやすい形に整理したもの
  • メリット
    • 再利用性の高い柔軟な設計ができる
    • 技術者どうしの意思疎通が容易になる

GoFデザインパターン

  • 生成に関するパターン
    • Abstract Factory
    • Builder
    • Factory Method
    • Prototype
    • Singleton
  • 構造に関するパターン
    • Adapter
    • Bridge
    • Composite
    • Decorator
    • Facade
    • Flyweight
    • Proxy
  • 振る舞いに関するパターン
    • Chain of Responsibility
    • Command
    • Interpreter
    • Iterator
    • Mediator
    • Memento
    • Observer
    • State
    • Strategy
    • Template Method
    • Visitor

Abstract Factory

  • 関連したオブジェクトを間違いなく生成するためのパターン
interface Factory {
  getSoup(): object
  getVegetables(): object
}
class KimuchiFactory implements Factory {
  getSoup(): object { return 'キムチスープ' }
  getVegetables(): object { 'キムチ,しめじ' }
}
class Pot {
  addSoup(soup: object) { /* ... */ }
  addVegetables(vegetables: object) { /* ... */ }
}
function createFactory(type: string): Factory {
  if (type === 'キムチ鍋') {
    return new KimuchiFactory()
  } else {
    return /* ... */
  }
}
function main() {
  const factory = new createFactory('キムチ鍋')
  const pot = new Pot()
  pot.addSoup(factory.getSoup())
  pot.addVegetables(factory.getVegetables())
}

Builder

  • オブジェクトの作成過程をコントロールするためのパターン
interface Builder {
  makeTitle();
  makeString();
  close();
  getResult<T>(): T;
}
class Director {
  constructor(builder: Builder) {
    this.builder = buidler;
  }
  construct() {
    this.builder.makeTitle('場所');
    this.builder.makeString('東京都港区 1-1-1');
    this.builder.makeTitle('日時');
    this.builder.makeString('2999/01/23');
    close();
  }
}
function main() {
  const builder = new HtmlBuilder();
  const director = new Director(builder);
  director.construct();
  const html = builder.getResult<Html>();
}

Factory Method

  • 柔軟にオブジェクトを生成するためのパターン
abstract class Factory {
  abstract createAccount(name: string): Account;
  create(name: string): Account {
    const account = new createAccount(name)
    return account;
  }
}
interface Account {
  type: string;
  name: string;
}
class AdminFactory extends Factory {
    createAccount(name: string): Account {
      const account = new Account();
      account.type = 'admin';
      account.name = name;
      return account;
    }
}
function main() {
  const factory = new AdminFactory();
  const admin1 = factory.create('山田 太郎');
  const admin2 = factory.create('佐藤 次郎');
}

Prototype

  • あらかじめ用意しておいた「原型」からインスタンスを生成するようにするためのパターン
interface Clonable {
  createClone(): Clonable;
}
class Manager {
  hashMap: Map = new Map();
  register(name: string, cloneable: Clonable) {
     this.hashMap[name] = cloneable;
  }
  create(name: string): Clonable {
    return this.hashMap[name].createClone();
  }
}
function main() {
  const manager = new Manager();
  manager.register('class a', new ClassA());
  manager.register('class b', new ClassB());

  const a = manager.create('class a') as ClassA;
  const b = manager.create('class b') as ClassB;
}

Singleton

class Singleton {
  static singleton: Singletone = new Singletone();
  private constructor() {}
  getInstance() {
    return this.singleton;
  }
}

Adapter

  • インタフェースに互換性の無いクラス同士を組み合わせることを目的としたパターン
interface Student {
  getFullName(): string;
}
class Child {
    constructor(firstName: string, lastName: string) {
      this.firstName = firstName;
      this.lastName = lastName;
    }
}
// 継承を利用したパターン
class ChildAdapter extends Child implements Student {
  getFullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }
}
// 委譲を利用したパターン
class ChildAdapter implements Student {
  constructor(firstName: string, lastName: string) {
    this.child = new Child(firstName, lastName);
  }
  getFullName(): string {
    return `${this.child.firstName} ${this.child.lastName}`;
  }
}
function main() {
  const student = new ChildAdapter('Taro', 'Yamada')
  student.getFullName();
}

Bridge

  • 機能を拡張するクラスと実装を拡張するクラスを分けるためのパターン
class Sorter {
  constructor(sortImpl: SortImpl) {
    this.sortImpl = sortImpl
  }
  sort(array: Array) {
    this.sortImpl.sort(array);
  }
}
class ImmutableSorter {
  immutableSort(array: Array): Array {
    const clonedArray = array.clone();
    return this.sortImple.sort(clonedArray);
  }
}
interface SortImpl {
  sort();
}
class QuickSortImpl implements SortImpl {
  sort() { /* クイックソート処理 */ }
}
class BubbleSortImpl implements BubbleSortImpl {
  sort() { /* バブルソート処理 */ }
}
function main() {
  new Sorter(new QuickSortImpl()).sort([5,1,2])
  new Sorter(new BubbleSortImpl()).sort([5,1,2])

  const arr1 = new ImmutableSorter(new QuickSortImpl()).immutableSort([5,1,2])
  const arr2 = new ImmutableSorter(new BubbleSortImpl()).immutableSort([5,1,2])
}

Composite

  • 容器と中身を同一視することで再帰的な構造を扱いやすくするためのパターン
interface Entry {
  getName(): string;
  remove();
}
class File implements Entry {
  constructor(name: string) { this.name = name }
  getName(): string { return this.name }
  remove() { console.log(`${this.getName()}を削除しました`) }
}
class Directory implements Entry {
  constructor(name: string) {
    this.name = name
    this.entries = []
  }
  getName(): string { return this.name }
  remove() {
    for (const entry of this.entries) {
      entry.remove()
    }
    console.log(`${this.getName()}を削除しました`)
  }
  add(entry: Entry) {
    this.entries.push(entry)
  }
}
function main() {
  const rootDir = new Directory('root')
  const homeDir = new Directory('home')

  homeDir.add(new File('.bashrc'))
  rootDir.add(homeDir)
  rootDir.remove()
}

Decorator

  • 飾り枠と中身を同一視することで、柔軟に機能拡張しやすくするためのパターン
interface Ice {
  getName(): string
}
class VanillaIce implements Ice {
  getName(): string { return 'バニラアイス' }
}
class NutsToppingIce implements Ice {
  constructor(ice: Ice) { this.ice = ice }
  getName(): string {
    return `ナッツ ${this.ice.getName()}`
  }
}
class ChocoToppingIce implements Ice {
  constructor(ice: Ice) { this.ice = ice }
  getName(): string {
    return `チョコ ${this.ice.getName()}`
  }
}
function main() {
  new VanillaIce().getName() // バニラアイス
  new NutsToppingIce(new VanillaIce()).getName() // ナッツ バニラアイス
  new ChocoToppingIce(new NutsToppingIce(new VanillaIce())).getName() // チョコ ナッツ バニラアイス
}

Facade

  • 複数クラス組み合わせて行う処理を、窓口となるクラスを作ってシンプルに利用できるようにするためのパターン
  class HtmlWriter {
    setTitle(title: string) { this.title = title }
    setBody(body: string) { this.body = body }
    output(): string {
      return `<html>
                <head>${this.title}</head>
                <body>${this.body}</body>
              </html>`
    }
  }
  class FileWriter {
    create(file: string, content: string) { /* ファイル書き込み処理 */ }
  }
  class PageMaker {
    makeWelcomePage(title: string, file: string) {
      const htmlWriter = new HtmlWriter()
      htmlWriter.setTitle(title)
      htmlWriter.setBody(`ようこそ ${title} へ!!`)

      const fileWriter = new FileWriter()
      fileWriter.create(file, fileWriter.output())
    }
  }
  function main() {
    const pageMaker = new PageMaker()
    pageMaker.makeWelcomePage('ABC', 'welcome.html')
  }

Flyweight

  • インスタンスを共有することでリソースを無駄なく使うためのパターン
class Stamp {
  constructor(value: string) { this.value = value }
  print() { console.log(this.value) }
}
class StampFactory {
  stamps = {}
  get(value: string): Stamp {
    if (!this.stamps[value]) {
      this.stamps[value] = new Stamp(value)
    }
    return this.stamps[value]
  }
}
function main() {
  const factory = new StampFactory()
  const stamps = [
    factory.get('a'),
    factory.get('p'),
    factory.get('p'),
    factory.get('l'),
    factory.get('e'),
  ]
  for (const stamp of stamps) {
    stamp.print()
  }
}

Proxy

  • 作業に時間がかかっているオブジェクトの代わりに
interface Image {
  render()
}
class RealImage implements Image {
  constructor(path: string) { /* 画像読み込み処理 */ }
  render() { /* 画像を表示 */ }
}
class LazyLoadImage implements Image {
  constructor(path: string) { this.path = path }
  render() {
    const realImage = new RealImage(path)
    realImage.render()
  }
}
function main() {
  const image1 = new LazyLoadImage('image1.png')
  const image2 = new LazyLoadImage('image2.png')
  const image3 = new LazyLoadImage('image3.png')

  image1.render() // 画像が読み込まれ表示される
  image2.render() // 画像が読み込まれ表示される
  // image3の不要な読み込み処理は行われない
}

Chain of Responsibility

  • 「責任者」を「鎖状」につないでおき、「いずれかの段階」で、「誰か」が処理をすることを表現するためのパターン
abstract class Responsible {
  setNext(next: Responsible) { this.next = next }
  salaryRaise(score: int): boolean {
    if (this.isMoreThanExpected(store)) {
      if (this.next) {
        return this.next.salaryRaise()
      } else {
        return true
      }
    } else {
      return false
    }
  }
  abstract isMoreThanExpected(store: int): boolean
}
class Staff extends Responsible {
  isMoreThanExpected(score: int) {
    return score > 50
  }
}
class TeamLeader extends Responsible {
  isMoreThanExpected(score: int) {
    return score > 70
  }
}
class Manager extends Responsible {
  isMoreThanExpected(score: int) {
    return score > 90
  }
}
function main() {
  const staff = new Staff()
  const teamLeader = new TeamLeader()
  const manager = new Manager()

  staff.setNext(teamLeader).setNext(manager)
  staff.salaryRaise(80) // false
  staff.salaryRaise(95) // true
}

Command

  • 要求をCommandオブジェクトにして、それらを複数組み合わせて使えるようにするパターン
interface Command {
  execute()
}
class SaladCommand implements Command {
  execute() { /* サラダを調理 >> 提供 */ }
}
class CurryCommand implements Command {
  execute() { /* カレーを調理 >> 提供 */ }
}
class SoupCommand implements Command {
  execute() { /* スープを調理 >> 提供 */ }
}
class Order {
  constructor() {
    this.commands = []
  }
  add(command: Command) {
    this.commands.push(command)
  }
  confirm() {
    for (const command of this.commands) {
      command.execute()
    }
  }
}
function main() {
  const order = new Order()
  order.add(new SaladCommand())
  order.add(new CurryCommand())
  order.add(new SoupCommand())
  order.confirm()
}

Interpreter

Iterator

  • 要素の集まりを保有するオブジェクトの各要素に順番にアクセスする方法を提供するためのパターン
interface Iterator {
  hasNext(): boolean
  next(): any
}
interface Aggregate {
  iterator(): Iterator
}
class UserListIterator implements Iterator {
  index: 0
  constructor(userList: UserList) { this.userList = userList }
  hasNext(): boolean { return (index < users.getSize()) }
  next(): any { return this.getAt[index++] }
}
class UserList implements Aggregate {
  users: []
  add(user: string) { this.users.push(user) }
  getSize(): int { return this.users.length }
  getAt(index): string { this.users[index] }
  iterator(): Iterator { return new UserListIterator(this) }
}
function main() {
  const userList = new UserList()
  userList.add ('田中')
  userList.add('佐藤')
  userList.add('山田')

  const iterator = userList.iterator()
  while (iterator.hasNext()) {
    console.log(iterator.next())
  }
}

Mediator

  • 複数のオブジェクト間の調整をするために、仲介役のクラスを利用するパターン
interface ChatRoomMediator {
  showMessage(user: User, message: string)
}
class ChatRoom implements ChatRoomMediator {
  showMessage(user: User, message: string) {
    console.log(user.name, message)
  }
}
class User {
  constructor(name: string, mediator: Mediator) {
    this.name = name
    this.mediator = mediator
  }
  send(message: string) {
    this.mediator.showMessage(this, message)
  }
}
function main() {
  const mediator = new ChatRoom()

  const tom = new User('Tom', mediator)
  const bob = new User('Bob', mediator)

  tom.send('Hello')
  bob.send('Hi')
}

Memento

  • インスタンスの状態を保存し、後から状態を復元するためのパターン
class EditorMemento {
  constructor(content: string) { this.content = content }
  getContent() { return this.content }
}
class Editor {
  content = ''
  type(text: string) { this.content += text }
  getContent(): string { return this.content }
  save(): EditorMemento { return new EditorMemento(this.content) }
  restore(mement: EditorMemento) { this.content = mement.getContent() }
}
function main() {
  const editor = new Editor()

  editor.type('abc')
  editor.type('def')
  const saved = editor.save()

  editor.type('ghi')
  editor.getContent() // abcdefghi

  editor.restore(saved)
  editor.getContent() // abcdef
}

Observer

  • 通知の仕組みをより汎用的に利用できる形で提供するためのパターン
interface Observer {
  update(jobPost: string)
}
class JobSeeker implements Observer {
  update(jobPost: string) {
    console.log(`${jobPost}の求人が追加されました`)
  }
}
interface Subject {
  notify(jobPost: string)
}
class JobPostings implements Subject {
  attach(jobSeeker: JobSeeker) { this.jobSeekers.push(jobSeeker) }
  addJob(jobPost: string) { this.notify(jobPost) }
  notify(jobPost: string) {
    for (const jobSeeker of this.jobSeekers) {
      jobSeeker.update(jobPost)
    }
  }
}
function main() {
  const jobSeeker = new JobSeeker()
  const jobPostings = new JobPostings()
  jobPostings.attach(jobSeeker)

  jobPostings.addJob('エンジニア') // エンジニアの求人が追加されました
  jobPostings.addJob('デザイナー') // デザイナーの求人が追加されました
}

State

  • 状態に応じて振る舞いを変化させるためのパターン
interface State {
  write(text: string)
}
class UpperCaseState implements State {
  write(text: string) { console.log(text.toUpperCase()) }
}
class LowerCaseState implements State {
  write(text: string) { console.log(text.toLowerCase()) }
}
class TextEditor {
  setState(state: State) { this.state = state }
  type(text: string) { this.state.write(text) }
}
function main() {
  const editor = new TextEditor()

  editor.setState(new UpperCaseState())
  editor.type('Hello World') // HELLO WORLD

  editor.setState(new LowerCaseState())
  editor.type('Hello World') // hello world
}

Strategy

interface SortStrategy {
  sort(array: [])
}
class BubbleSortStrategy implements SortStrategy {
  sort(array: []) { /* バブルソート処理 */ }
}
class QuickSortStrategy implements SortStrategy {
  sort(array: []) { /* クイックソート処理 */ }
}
class Sorter {
  constructor(strategy: SortStrategy) { this.strategy = strategy }
  sort(array: []) { this.strategy.sort(array) }
}
function main() {
  const array = [1, 5, 3, 2]

  const sorter1 = new Sorter(new BubbleSortStrategy())
  sorter1.sort(array)

  const sorter2 = new Sorter(new QuickSortStrategy())
  sorter2.sort(array)
}

Template Method

  • スーパークラスで処理の枠組みを定め、サブクラスでその具体的内容を実装するパターン
abstract class AppBuilder {
  build() {
    this.test()
    this.assemble()
    this.deploy()
  }
  abstract test()
  abstract assemble()
  abstract deploy()
}
class AndroidBuilder extends AppBuilder {
  test() { /* Androidテスト処理 */ }
  assemble() { /* Androidアセンブル処理 */ }
  deploy() { /* Androidデプロイ処理 */ }
}
class IosBuilder extends AppBuilder {
  test() { /* iOSテスト処理 */ }
  assemble() { /* iOSアセンブル処理 */ }
  deploy() { /* iOSデプロイ処理 */ }
}
function main() {
  const androidBuilder = new AndroidBuilder()
  androidBuilder.build()

  const iosBuilder = new IosBuilder()
  iosBuilder.build()
}

Visitor

  • 受け入れる側に処理を追加することなく、処理を追加することができるパターン
interface AnimalOperation {
  visitMonkey(monkey: Monkey)
  visitLion(lion: Lion)
}
interface Animal {
  accept(operation: AnimalOperation)
}
class Monkey implements Animal {
  shout() { console.log('ウキャッ、キャッ') }
  accept(operation: AnimalOperation) {
    operation.visitMonkey(this)
  }
}
class Lion implements Animal {
  roar() { console.log('ガオォオオ') }
  accept(operation: AnimalOperation) {
    operation.visitLion(this)
  }
}
class BarkOperation implements AnimalOperation {
  visitMonkey(monkey: Monkey) {
    monkey.shout()
  }
  visitLion(lion: Lion) {
    lion.roar()
  }
}
function main() {
  const bark = new BarkOperation()

  const monkey = new Monkey()
  monkey.accept(bark)

  const lion = new Lion()
  lion.accept(bark)
}

SOLID原則

  • Single responsibility principle
    • あるモジュールやクラスや関数などを改修する理由はたった1つになるようにする
    • 1つのクラスが複数の役割を持っている→変更時に他の役割にも影響を与えてしまう
  • Open-closed principle
    • 拡張に対してオープンで、修正に対してクローズドにする
    • 機能を追加する時に既存コードの修正が必要→既存の振る舞いが変わってしまう
  • Liskov substitution principle
    • 子クラスは親クラスと置き換え可能にする
    • 置換可能でない→抽象以外に依存している→密結合になってしまう
  • Interface segregation principle
    • インターフェースを複雑にしてはいけないので、分離できるものは分離する
    • インターフェースが複雑→複数の役割を持っている→変更時に受ける影響が大きくなる
  • Dependency inversion principles
    • 依存方向を逆転させることで、設計上望ましい依存の方向性にする
    • 望ましい依存の仕方→双方向依存しない・依存関係が循環しない・必要以上の情報を受け取らず疎結合

UML(Unified Modeling Language)

  • ソフトウェアの機能や構造を表す「図」の描き方
  • モデリング
    • 言葉で表現しきれない抽象的・複雑・あいまいな対象を、目的に合わないもの除きシンプルにした上で、「模型」や「図」など視覚的に表現すること
  • ダイアグラム
    • 構造
      • クラス図
        • クラス間の関係や、各クラスの変数などを表現したもの
      • 複合構造図
      • コンポーネント
      • オブジェクト図
      • パッケージ図
        • パッケージ間の関係を表現したもの
    • 振る舞い
      • ユースケース
        • システムが提供する機能と利用者の関係を表現したもの
      • アクティビティ図
        • 一連の処理における制御の流れを表現したもの
      • ステートマシン図
    • 相互作用
      • シーケンス図
        • オブジェクト間の連携や動作の流れを表現したもの
      • コミュニケーション図
      • 相互作用概要図
      • タイミング図
ダイアグラム名 サンプル
クラス図 f:id:umatomakun:20200125123547g:plain
オブジェクト図 f:id:umatomakun:20200125123606g:plain
パッケージ図 f:id:umatomakun:20200125123618p:plain
ユースケース f:id:umatomakun:20200125123629j:plain
アクティビティ図 f:id:umatomakun:20200125123642j:plain
シーケンス図 f:id:umatomakun:20200125123654g:plain
コミュニケーション図 f:id:umatomakun:20200125123706g:plain

参考資料

アジャイル検定Lv.2 勉強メモ - 常時結合

アジャイル検定Lv.2 出題範囲

常時結合

CI/CD

  • アプリケーション開発のステージに自動化を取り入れて、顧客にアプリケーションを提供する頻度を高める手法

継続的インテグレーション(CI)

  • ビルドと単体テストおよび統合テストを自動化
  • 単体テストと統合テストを行い、変更によってアプリケーションが破壊されないことを確認する
  • CIを利用するとコンフリクトやバグをすばやく容易に、高頻度で修復できる

継続的デリバリー(CD)

  • 検証されたコードのリポジトリへのリリースを自動化
  • 開発者によるアプリケーションへの変更をリポジトリにアップロードする
  • 本番環境にデプロイできるコードベースを常に保持しておくことを目的とする
  • プロセスの終了時には、運用チームはアプリケーションを本番環境にすばやく簡単にデプロイできる

継続的デプロイメント(CD)

  • アプリケーションの番環境へのリリースを自動化
  • 開発者による変更をリポジトリから本番環境に自動的にリリースし、顧客が使用できるようにする
  • ユーザーからのフィードバックを継続的に受け取って反映することが簡単になる

参考資料

アジャイル検定Lv.2 勉強メモ - テスト

アジャイル検定Lv.2 出題範囲

テスト

テスト駆動開発(TDD)

  • 「動作するきれいなコード」を書くのが目的
  • TDDサイクル
    • Red → Green → Refactor
    • テストを書く → 動かす → 正しくする

モックを使ったテスト

コードカバレッジ

  • ユニットテストの品質基準
    • C0(命令網羅)
      • プログラム中の全ての命令が1回以上実行を測定
    • C1(分岐網羅)
      • プログラム中の各分岐について1回以上実行を測定
    • C2(条件網羅)
      • プログラム中の判定の組み合わせを測定

テスト技法

  • ホワイトボックステスト
    • 内部ロジックや仕様を考慮してテストケースを設計
    • 内部仕様の細かい粒度で検証するテストで使われる
  • ブラックボックステスト
    • 外部仕様からテストケースを設計
    • 機能単位などの大きな粒度で検証するテストで使われる

アジャイルテストの4象限 f:id:umatomakun:20200117234311p:plain

参考資料