読書ログ

Land of Lisp

Land of Lisp

懲りずに。Lisp チャレンジ。まだ4章まで。
プログラミング本なのだが、 ストーリーがあって絵が多いので、中学生、高校生も、楽しめるんじゃなかろうか。

最後のエピローグに Lisp の特徴を 7つのギルドの宇宙艇に見立てて紹介している。
サクッと Lispについて、 自分の把握していない、理解していない何かがあるということがわかって便利だった。

読書ログ

インテルの製品開発を支えるSFプロトタイピング (プロフェッショナル&イノベーション)

インテルの製品開発を支えるSFプロトタイピング (プロフェッショナル&イノベーション)

SFに惹かれて購入した。 初期のSFの歴史(小説、映画、コミック)の勉強にもなる。新製品開発の際に、ストーリーテーリングの技法に、 SF のテイストを加えた感じ。 さらっと書いたが、ネジを飛ばした新しいアイデアを練りたいなら、試してみていいんじゃね。

Value Object の学習メモ

私の Value Object の学習メモ

おれ、アナパタから入ったかも。

DDD

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

私が、 Value Object について、再考するきっかけになったのは,やっぱりこの本。Entity と Value Object の違いといえば、アイデンティティを持つか持たないか、Value Objectはイミュータブルなど。サンプルだと、「顧客」の中に埋もれていた概念「住所」を Value Object として切り出し、よりドメインを明瞭にするという、ごくごく一般的な例が出てくる。


興味を引きつけたのは、8章のブレークスルーの物語。物語を要約すると、ドメインエキスパートと話が噛み合わない日々が続く。が、やがて、対話を通じて「シェアパイ」という重要概念を探り当てる。「そうそう、それ!」 シェアパイの概念を使った実装に代えるため、(現行スケジュールが遅れているにもかかわらず)マネージャーと交渉し了解を得て、実装する。後日談で、やがて、シェアパイの概念が 営業の人たちにも使われていく話(要約すると味気ないなw)。 実世界 -> ドメインモデルの1方向写像ではなく、ドメインモデル->実世界の逆方向写像の話になっていて、読んでいて、ドキドキする。(著者は渦巻き好きなので、ドメインモデルと実世界は循環して捉えていると推測される。)

この物語の重要概念のシェアパイを探りあてる補助線にあるのが、 Value Object。Supple Design も読んでおくと、Value Object とその周辺で役立つパターン(閉じた操作、副作用のない関数)を理解でき、8章がより楽しく読める。

DDDを読んでいると、Value Objectを使って、重要概念を明瞭にすることに、強いこだわりを感じる。

GOOS

実践テスト駆動開発 (Object Oriented SELECTION)

実践テスト駆動開発 (Object Oriented SELECTION)

p16 に 値とオブジェクトの話が出てくる。また、p63から、値型を導入するテクニックとして、分解、発芽、包括が出てくる。
私の場合だと、 「String型、int型の基本データ型に執着していないか?重要概念を見落としていなか?」は時々考えるようになった。多分「発芽」に相当するもの。 DDDのシェアパイの話は、分解か包括か区別はわからなかった。

3部のサンプルの中では、最初ベタ書きだったコードを Value Objectを導入してコードをスッキリさせるシーンが何度か出てくる。

好きな文章は p16にある。

これはつまり、私たちはシステムをふたつの「世界」に分けたいということだ。
ひとつは関数的に扱われる値、もうひとつはシステムのステートフルな振る舞いを実装したオブジェクトだ

実践テスト駆動開発は、アランケイが構想した、オブジェクト同士のメッセージのやりとり重要を強調していることから、後者のオブジェクトがメインの語り口であるものの、前者の「値(Value Object)」についてもキチンと取り扱っており、時折出てくる。(関数型言語は、前者の世界を強調するだろう。Value Objectよりももっとうまく世界を表現できるはず。)

TDDの素振り
  • ローマ数字の足し算や引き算を素振りする。
  • ライフゲームを3回ぐらい解いてみる。4回目は immutable only の縛りで実装してみる。

あたりは、Value Object を再考するきっかけ例題としては良いんじゃなかろうか。


追記

Ruby で実装する場合
class Money < Struct.new(:amount, :currency)

と Structを使う手があることを知った。

別の Moneyの もりもりした実装例を覗いてみるのも手。

アプリケーションフレームワークの文法を妄想してた

rspec の文法を見ていると、 class XxxService, class YyyPolicy, class Zzz 。。。などを プロダクションコードから class 消し去りたい衝動が出てくる。
DDD, DCIを意識すると、こんなのが欲しい。

サービス
service Xxx, ' サービスの要約説明':
    before:
          ....
    exec 'サービスの振る舞いの要約説明':
          ....
    after:
         (成功失敗で分けたほうがいいかな。。。)
エンティティ
entity Zzz:
  (has_manyなどが使える)
ロール
role RoleName, Zzz:
   behave 'ロールの振る舞いの要約説明':
バリューオブジェクト
value Aaa(...):
    (.. ValueObject を書きやすくする なにかってあったけ。
Embedded Value, イミュータブル, 閉じた操作, 副作用のないメソッド...etc...)
ポリシー
policy Yyy(...):
    assert "ポリシーの説明":
       ...
       (Hamcrestのような述語が使える)
リポジトリ

。。。

みたいに、 アプリケーションを作成するための文法が欲しい。

Cucumberを使って iPhone のテスト

Frankというやつがある。

一番上のプログが一番役立った。

githubにあった iosアプリを例に 素振りしてみた。
https://github.com/haru01/cheddar-ios

動かし方は Readme を参考に。(cheddarのユーザ登録、https://cheddarapp.com/developer/appsを参考に、CDIDefines.mの修正が必要。 )


記述ポイントは、次のファイル
https://github.com/haru01/cheddar-ios/blob/master/Frank/features/register_modify_delete_todo.feature

# encoding:utf-8
Feature: TODO List

Scenario: タスクを追加編集削除できる
  Given I launch the app
  Then タスクリストを登録できる
  Then タスクを登録できる
  Then タスク名を編集できる
  Then タスクを削除できる
  Then タスクリストを削除できる

https://github.com/haru01/cheddar-ios/blob/master/Frank/features/step_definitions/tasks_steps.rb

# encoding:utf-8
World(TaskStepHelper)
When /^I type keybord  "(.*?)" and enter$/ do |key|
  type_into_keyboard key
end


Then /^タスクを登録できる$/ do
  step %Q|I touch "#{todo_list_name}"|
  step %Q|I wait to see "#{what_do_you_have_to_do}"|
  step %Q|I touch "#{what_do_you_have_to_do}"|
  step %Q|I wait for 1 second|
  step %Q|I type keybord  "#{first_task_name}" and enter|
end

Then /^タスク名を編集できる$/ do
  step %Q|I touch "#{lists_button}"|
  step %Q|I touch "#{todo_list_name}"|
  step %Q|I touch "#{edit_button}"|
  step %Q|I touch "#{first_task_name}"|
  step %Q|I wait for 1 second|
  step %Q|I type keybord  "EF" and enter|
end

Then /^タスクを削除できる$/ do
  step %Q|I wait for 1 second|
  step %Q|I touch "#{lists_button}"|
  step %Q|I touch "#{todo_list_name}"|
  step %Q|I touch "#{edit_button}"|
  step %Q|I touch "Delete"|
  step %Q|I touch "#{achirve_button}"|
  step %Q|I touch "#{lists_button}"|
end

Then /^タスクリストを登録できる$/ do
  step %Q|I touch "#{lists_button}"|
  step %Q|I touch "#{plus_button}"|
  step %Q|I wait for 1 second|
  step %Q|I type keybord  "#{todo_list_name}" and enter|
end

Then /^タスクリストを削除できる$/ do
  step %Q|I touch "#{edit_button}"|
  step %Q|I touch "Delete"|
  step %Q|I touch "#{achirve_button}"|
end

既にあるステップは、下記が 参考になる。

iOS独特のセレクターでうまく、画面要素を指定するキーを見つけるのがちょっとわかりにくい。
一応、要素をさがすためのinspectorも付いてくるのでそこから探すのだが。。。。

$frank inspect

Objective-Cの練習

#import "Kiwi.h"
#import "Underscore.h"

#define _ Underscore

@interface RomanNumber : NSObject

@property (readonly) NSNumber *number;
@property (readonly) NSString *romanStr;

@end

@implementation RomanNumber

-(id)initWithNum:(NSNumber *)aNumber {
    // TODO 引数が 1-3999 以外の場合
    self = [super init];
    if (self) {
        _number = aNumber;
        _romanStr = [self convertRomanStr:_number];
    }
    return self;
}

-(id)initWithRomanStr:(NSString *)aRomanStr {
    // TODO 引数がローマ数字でないばあい
    self = [super init];
    if (self) {
        _romanStr = aRomanStr;
        _number = [self convertNumber: _romanStr];
    }
    return self;
}

-(NSString *) toString {
    return [self convertRomanStr:_number];
}


-(NSNumber *) convertNumber:(NSString *)aRomanStr {
    // 総当たりでマッチするものを探す方法
    for(int i=0; i<4000; i++ ) {
        NSNumber *n = @(i);
        NSString *other = [self convertRomanStr:@(i)];
        if ([other isEqualToString: aRomanStr]) {
            return n;
        }
    }
    return @(-1);
}

-(NSString *) convertRomanStr:(NSNumber *)aNumber {
    NSDictionary *nums = @{
       @1: @"I",
       @4: @"IV",
       @5: @"V",
       @9: @"IX",
       @10: @"X",
       @40: @"XL",
       @50: @"L",
       @90: @"XC",
       @100: @"C",
       @400: @"CD",
       @500: @"D",
       @900: @"CM",
       @1000: @"M",
    };
    id romanStr = [nums objectForKey:aNumber];
    if (romanStr) {
        return romanStr;
    }
    
    for (NSNumber *base in [self keysDesc:nums]) {
        int diff = [aNumber intValue] - [base intValue];
        if (diff > 0) {
            return [NSString stringWithFormat:@"%@%@",[self convertRomanStr:base], [self convertRomanStr:[NSNumber numberWithInt:diff]]];
        }
    }
    return @"";
}

-(NSArray *) keysDesc:(NSDictionary *) dictionary {
    NSMutableArray *keysDesc =[NSMutableArray arrayWithArray:Underscore.keys(dictionary)];
    [keysDesc sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        return [obj2 compare:obj1];
    }];
    return keysDesc;
}

-(RomanNumber *) plus:(RomanNumber *) other {
    return [[RomanNumber alloc] initWithNum:@([self.number intValue] + [other.number intValue])];
}
- (BOOL)isEqual:(id)other {
    if (other == self) {
        return YES;
    }
    if (!other || ![other isKindOfClass:[self class]]) {
        return NO;
    }
    if ([[self number] intValue] != [[other number] intValue] ) {
        return NO;
    }
    return YES;
}
@end


SPEC_BEGIN(RomanNumberSpec)

describe(@"RomanNumber", ^{
    
    // 数字をローマ数字に変換できること
    _.dictEach(@{
               @1: @"I",
               @4: @"IV",
               @5: @"V",
               @9: @"IX",
               @10: @"X",
               @40: @"XL",
               @50: @"L",
               @90: @"XC",
               @100: @"C",
               @400: @"CD",
               @500: @"D",
               @900: @"CM",
               @1000: @"M",

               @3: @"III",
               @6: @"VI",
               @11: @"XI",
               @39: @"XXXIX",
               @44: @"XLIV",
               @95: @"XCV",
               @345: @"CCCXLV",
               @3999: @"MMMCMXCIX",
               }, ^(NSNumber *number, NSString *expected) {
        NSString *itStr = [NSString stringWithFormat:@"数字:%@をローマ数字:%@に変換できること", number, expected];
        it(itStr, ^{
            RomanNumber *roman =  [[RomanNumber alloc] initWithNum:number];
            [[[roman romanStr] should] equal: expected];
        });
    });

    it(@"ローマ数字の文字列から数値変換ができること", ^{
        RomanNumber *romanA =  [[RomanNumber alloc] initWithRomanStr:@"CCCXLV"];
        [[[romanA number] should] equal: @345];

        RomanNumber *romanB =  [[RomanNumber alloc] initWithRomanStr:@"MMMCMXCIX"];
        [[[romanB number] should] equal: @3999];
    });

    it(@"ローマ数字の足し算ができること", ^{
        RomanNumber *one =  [[RomanNumber alloc] initWithRomanStr:@"I"];
        RomanNumber *three =  [[RomanNumber alloc] initWithRomanStr:@"III"];
        RomanNumber *four =  [[RomanNumber alloc] initWithRomanStr:@"IV"];
        [[[one plus:three] should] equal:four];
        [[[[one plus:three] number] should] equal:@(4)];
    });
    
    // TODO
    // - 引き算
    // - ローマ数字のソート
});

SPEC_END

文法がちょっと覚えにくい。修正版は下記に。
https://github.com/haru01/objective-c_kiwi_testsample

成長ループとその成長を制限するバランスループ

フィールドブック 学習する組織「10の変革課題」―なぜ全社改革は失敗するのか?

フィールドブック 学習する組織「10の変革課題」―なぜ全社改革は失敗するのか?

この本、システム原型だと「成長の限界」を軸に描かれている。学習、チェンジマネジメント、改革、改善をしていこうとする時に、よく発生する課題をシステム思考を使って整理し、どんな思い込み(メンタルモデル)に注意すべきか、全体のつながりとしての背景やバランスループ(課題)を理解し解決の糸口となるレバレッジポイントがどこにあるのか、バランスループ(課題)に対処していく典型的なワークショップ案にどのようなものがあるかが解説されている。

読み返したのは、はじめにから、課題1: 時間がない「こんなことをしている時間はない!」, 課題2: 孤立無援「誰も助けてくれない!」, 課題3:意味がない「こんなものは意味がない!」, 課題4: 言行不一致「言っていることとやっていることが違う!」, 課題:5 恐れと不安 「こんなものは○○○だ! 」 課題:6 評価と測定 「効果がない! 」 まで。

繰り返し読んでいたつもりだが、前回までは身近に感じていなかった。今回ようやく自分の身にかなり近い課題だと実感がわいてきた。どの課題も過去に(今も)何度見てきた「あるある」ネタになっている。

「時間がない」は、かならずは聞く言葉なのでヒントがある。押し売りで始まる改革は大抵「意味がない」に打ち当たる。年を食って上の立場、指導する立場になる人であれば、「言行不一致」には注意されたし。恐怖政治、コマンドコントロールからの脱却(メンタルモデルの克服)はなかなか難しいテーマ。「恐れと不安」にヒントがある。また通常新しいことにチャレンジするのであれば「恐れと不安」がつきものだ。サムライの「これは魔女狩りではない」の言葉の裏が見えてくる。 活動の周りへの説明に、評価と測定の課題は必ず付いてきて避けては通れない。まずは「評価」の言葉の意味の理解から。(報告よりも)自分たちの学習に使える楽しい「評価」にしてしていくことが肝要。