コードを短く書く努力をすると、テストが省ける

まぁ、がしがしとタイピングするのも楽しいけど、私は作業量を考えて短くするのが好きです。
行数にするとアウトプットの量が減るんですけどねw、気にせず。

こんな風な画面で、マウスをポイントしたとき(MouseEnter と MouseLeaveイベント)の時にステータスバーにメッセージを表示します。
あと、チェックボックスを切り替えた時に、各ボタンの有効/無効が変えられます。

ざっと、ソースコード。何故か VB で(笑)

Public Class Form1

	Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
		' 有効/無効を設定
		InitEnableMode()
		' 動的にイベントを追加
		AddHandler RadioButton1.CheckedChanged, AddressOf RadioButton1_CheckedChanged
		AddHandler RadioButton2.CheckedChanged, AddressOf RadioButton1_CheckedChanged
		AddHandler RadioButton3.CheckedChanged, AddressOf RadioButton1_CheckedChanged

		' ステータスバーの設定
		InitStatusBar()
		' イベントを追加
		Dim items As New List(Of Control)
		items.Add(Button1)
		items.Add(Button2)
		items.Add(Button3)
		items.Add(ComboBox1)
		items.Add(TextBox1)
		items.Add(TextBox2)
		items.Add(TextBox3)
		For Each c In items
			AddHandler c.MouseEnter, AddressOf Button1_MouseEnter
			AddHandler c.MouseLeave, AddressOf Button1_MouseLeave
		Next
	End Sub

	''' <summary>
	''' 有効/無効を設定
	''' </summary>
	''' <remarks></remarks>
	Public Sub InitEnableMode()
		Dim o As Boolean = True
		Dim x As Boolean = False

		'' Mode 1, 2, 3, コントロールの順で指定
		SETMODE(o, o, o, Button1)
		SETMODE(o, o, x, Button2)
		SETMODE(o, x, x, Button3)
		SETMODE(o, x, x, ComboBox1)
		SETMODE(o, x, x, TextBox1)
		SETMODE(x, o, o, TextBox2)
		SETMODE(x, o, o, TextBox3)

	End Sub

	Private _modes As New List(Of List(Of Object))
	Public Sub SETMODE(ByVal ParamArray params As Object())
		Dim max As Integer = params.Length
		Dim items As New List(Of Object)
		items.Add(params(max - 1))
		For i As Integer = 0 To max - 2
			items.Add(params(i))
		Next
		_modes.Add(items)
	End Sub

	Public Sub ChangeMode(ByVal mode As Integer)
		For Each items As List(Of Object) In _modes
			Dim c As Control = CType(items(0), Control)
			c.Enabled = CType(items(mode + 1), Boolean)
		Next
	End Sub
	Private Sub RadioButton1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
		If sender Is RadioButton1 Then
			ChangeMode(0)
		ElseIf sender Is RadioButton2 Then
			ChangeMode(1)
		ElseIf sender Is RadioButton3 Then
			ChangeMode(2)
		End If
	End Sub


	Class STATMSG
		Public cont As Control
		Public msg As String
	End Class
	Private _status As New List(Of STATMSG)
	''' <summary>
	''' フォーカス時のメッセージを設定
	''' </summary>
	''' <param name=&quot;c&quot;></param>
	''' <param name=&quot;msg&quot;></param>
	''' <remarks></remarks>
	Public Sub SETSTATUS(ByVal c As Control, ByVal msg As String)
		Dim sm As New STATMSG() With {.cont = c, .msg = msg}
		_status.Add(sm)
	End Sub

	Public Sub InitStatusBar()
		SETSTATUS(Button1, &quot;先頭のボタンです&quot;)
		SETSTATUS(Button2, &quot;真ん中のボタンです&quot;)
		SETSTATUS(Button3, &quot;一番舌のボタンです&quot;)
		SETSTATUS(ComboBox1, &quot;項目を選択します&quot;)
		SETSTATUS(TextBox1, &quot;名前を入力します&quot;)
		SETSTATUS(TextBox2, &quot;年齢を入力します&quot;)
		SETSTATUS(TextBox3, &quot;住所を入力します&quot;)
	End Sub

	Public Sub ChangeFocus(ByVal sender As Object, ByVal b As Boolean)
		If b = False Then
			ToolStripStatusLabel1.Text = &quot;&quot;
		Else
			For Each cm In _status
				If cm.cont Is sender Then
					ToolStripStatusLabel1.Text = cm.msg
				End If
			Next
		End If
	End Sub

	Private Sub Button1_MouseEnter(ByVal sender As System.Object, ByVal e As System.EventArgs)
		ChangeFocus(sender, True)
	End Sub
	Private Sub Button1_MouseLeave(ByVal sender As System.Object, ByVal e As System.EventArgs)
		ChangeFocus(sender, False)
	End Sub
End Class

眠たいので、詳しい解説は後ほどやる…かもしれず。

メモ的に書いておくと、モードの設定のフォーマットがミソです。

	''' <summary>
	''' 有効/無効を設定
	''' </summary>
	''' <remarks></remarks>
	Public Sub InitEnableMode()
		Dim o As Boolean = True
		Dim x As Boolean = False

		'' Mode 1, 2, 3, コントロールの順で指定
		SETMODE(o, o, o, Button1)
		SETMODE(o, o, x, Button2)
		SETMODE(o, x, x, Button3)
		SETMODE(o, x, x, ComboBox1)
		SETMODE(o, x, x, TextBox1)
		SETMODE(x, o, o, TextBox2)
		SETMODE(x, o, o, TextBox3)

	End Sub

bool 値を「o」と「x」に割り当てているのは、こんな風に表形式で設定したいからです。かつて、VC++ の時に作ったノウハウです。その時には、define を使ったり外部ファイルを include したりしたのですが、vb ではできません。ture, false を使ってもいいのですが、visual studio が勝手にフォーマットしてしまうから、桁がずれるんです。なので、こんな風に「o」と「x」を使うわけです。
なんとなく分かり易いですね。
モードが増えた場合は、行を増やせばいい訳です。

同じようにステータスバーの切り替えの設定も書けます。

	Public Sub InitStatusBar()
		SETSTATUS(Button1, "先頭のボタンです")
		SETSTATUS(Button2, "真ん中のボタンです")
		SETSTATUS(Button3, "一番舌のボタンです")
		SETSTATUS(ComboBox1, "項目を選択します")
		SETSTATUS(TextBox1, "名前を入力します")
		SETSTATUS(TextBox2, "年齢を入力します")
		SETSTATUS(TextBox3, "住所を入力します")
	End Sub

こんな風に、SETSTATUS 関数を作ります。クラスを作って new SetStatus() With { … } な形で書いてもよいのですが、new やらクラス名やらを設定するのが面倒ですよね。所詮、使い捨ての関数ですから、べたべたに書いてしまいます。

		' イベントを追加
		Dim items As New List(Of Control)
		items.Add(Button1)
		items.Add(Button2)
		items.Add(Button3)
		items.Add(ComboBox1)
		items.Add(TextBox1)
		items.Add(TextBox2)
		items.Add(TextBox3)
		For Each c In items
			AddHandler c.MouseEnter, AddressOf Button1_MouseEnter
			AddHandler c.MouseLeave, AddressOf Button1_MouseLeave
		Next

マウスイベントも、こんな風にまとめて書いてしまいます。Handles に書いてもいいのですが、数が少ないときはいいけどこんな風に多くなると大変ですよね。なので、使い捨てのリストを使ってどしどしと追加します。

なんで、私がこんなコードを書くのかというと、

  • 使い捨てのコードは、効率よりも分かり易く書く。
  • 分かり易く書くと、あとからの変更がしやすい。
  • コードを見て分かり易いと、テストを省略できる。

からです。
コントロールの有効/無効の処理なんかは、if 文と control 名を使ってずらずらと書き並べることはできるのですが、それをやってしまうと、

  • 抜けがあると、何処が悪いのか分かりづらい。
  • 抜けがでやすいので、テストする必要がある。

ってことが理由なのです。先のように(トリッキーではありますが)あたかも Excel で設定しているように x と o を使って桁を揃えていくと、抜けが明確になります。

コードを短くすると生産性が悪くなるという弊害がw出ますが、まぁ、タイピングするのが仕事ではないので、昔から短いコードをたくさん書きます。そう、プログラマの仕事はコードを書くことではなくて、動くプログラムを作ることですからね、ってことです。

カテゴリー: 設計, 雑談, xUnit | 3件のコメント

Gantt Chart を IDEF0 風に記述する

大雑把ですが、ガントチャートを IDEF0 風に分解すると、こんな図になります。

・インプット方向のタスク(前工程のタスク)がある。
・アウトプット方向のタスク(後工程のタスク)がある。
・タスクは、一定の作業量を持つ。
・タスクは、一定の作業を行うと終了する(開始と終了がある)。
・作業は、配員(人)が行う。
・配員は、一定の作業能力(属人性、能力差)を持つ。

・配員は、リソースとしての制約がある(同時に2つの場所で働けないなど)。
・タスクは、時間制約がある(無限に長い期間でやってはいけない)。
・タスクは、期間制約がある(無限に期限を延ばしてはいけない)。
・1日の時間は、24時間である(週単位の勤務時間は40時間である、など)。

ここで、前者の項目は「前提条件」であり、ある条件をクリアすると「完了」になります。RPG ゲームみたいなものです。

後者の制限は、制約条件で「ルール」になります。ルール違反をすることはできません…というか、ルール自体がないと「ルール違反」って現象がないですよね(と、現象学的な話とか)。

これだけの条件を出しておけば、プログラミング可能/検証可能になります。いわゆる XP で言うところの計画ゲームが可能です。

で、これをベースにして擬似コードを書いてみると。


// 制約条件を設定
Task.制約(&quot;40時間勤務&quot;);
Task.制約(&quot;残業なし&quot;);
Task.制約(&quot;リリース時期&quot;, 2012/01/01 );

// 属人性, 能力差ポイントを設定
Person(&quot;masuda&quot;).属人性(&quot;HP&quot;, 100 );
Person(&quot;masuda&quot;).属人性(&quot;MP&quot;, 200 );  // 魔法が使えるとか

Person(&quot;tomoaki&quot;).属人性(&quot;HP&quot;, 200 ); // 体力が多いとか
Person(&quot;tomoaki&quot;).属人性(&quot;MP&quot;,  50 ); 

// リソース(配員)を設定
Plan.リソース(Person(&quot;masuda&quot;),  0.5 );
Plan.リソース(Person(&quot;tomoaki&quot;), 1.0 );

// タスクの作業ポイントを設定
Task(&quot;本タスク&quot;).作業ポイント( 200 );

// 連携を設定
Task(&quot;前タスク&quot;) -> Task(&quot;本タスク&quot;);
Task(&quot;本タスク&quot;) -> Task(&quot;後タスク&quot;);

こういう風に設定しておいて、


// ガントチャートを作成して
Plan.CreateGanttChart();

シミュレートする場合は、こんな風に。


// こんな風にして、100 回ほどシミュレートする
Plan.Simulate(100) 

現実にプロジェクトの実績を入れる場合は、次のように。


// 実績を設定
Plan.Task("本タスク").実績( 100 );

// タスクが無くなったら、即削除して再計画
Plan.RemoveTask("削除タスク");
Plan.CreateGanttChart();

// タスクが増えたら、即追加して再計画
Plan.AddTask("追加タスク");
Plan.CreateGanttChart();

// タスクが追加されたので、シミュレーションをやり直してみる。
Plan.Simulate(100) 

ここで、成功確率なんかが出ると、実に「それらしい」感じになるんでうすがどうでしょう?

アジャイルが一見すると「計画」を捨てているように見えるのは、それぞれのタスク/ストーリーカードの繋がりを「視覚化」しないところにあります。これは、視覚化することによるオーバーヘッドを避ける≒コミュニケーションコストを下げる、ことに一役買っています。

なので、逆に言えば、計画を視覚化することのコストが限りなく低ければ、再計画時の情報となるので、不明確部分が減り、成功確率があがります(と予想されます)。まぁ、計画倒れとか取らぬ狸のなんとやら、というのもありますから「再計画」が絶対という訳ではありませんが、ひとつの選択肢が増えますよね、ってことで。

カテゴリー: プロジェクト管理, Plan Language | Gantt Chart を IDEF0 風に記述する はコメントを受け付けていません

verifiable plan language の目指すところを考察してみる

検証可能な計画言語ってことで、おおまかな目標のメモです。Javaのカラー本を真似してして色分けしてみました。「色」自体に意味があるのですが、これは後ほど。

ざっと見て分かるとおり、PDCA(Plan/Do/Check/Action)を元に情報の流れをチェックしています。データの流れも PDCA に順じているので、このフローを使うことになるのかなと。

■計画(Plan)

いきなり、Gantt Chart を作り始めるのは無謀なので、WBS なり、ストーリーカードなりで項目の抽出を行います。
作業項目の抽出は、主に 3 つの方法があります。

・PMBOK/WBS のようにトップダウンで
・XP/SCRUM のようにボトムアップで
・CCPM のようにエンドから

いずれにしても、「タスク抽出」という工程になりますね。このあたりは、plan なところで、抽出したタスクには、属性があります。

・作業量(人月/人日/人時間/作業ポイント/ストーリーポイント)

このタスクを実行する場合に、属人性の高さにより 3 つに分けられます。

・誰がやっても、一定時間かかる作業
・属人性がある作業(専門など)
・能力差がある作業(人依存)

属人性と能力差の例としては、

・そのプロジェクトに従事していた人であれば、○○時間で済むが、別の人をアサインすると××時間、これが「属人性」
・同じプログラミングでも、ベテランの人がやれば○○時間で済むが、新人がやれば××時間かかる、これが「能力差」

となります。属人性の場合は、年齢や経験よりも「専門性」が重視され、能力差の場合は「経験」が重視されるという形です。このあたりを加味しないと、正確なガントチャートはできませんよね、という具合です。

■実行(Do)

計画に従って実行する訳ですが、ここでは実際に実行する場合とシミュレーションする場合との 2 つに分けます。
シミュレーションの場合は、XP で言うところの「計画ゲーム」であったり「ブレーンストーミング」であったりします。
サイコロを転がしてゲーム的にやってもいいのですが、コンピュータのパワーを使ってランダム要素で、100 回ぐらいプロジェクトを稼動させれば、それなりに収束するような気もします。

このあたりは、ワインバーグ氏がシステムダイナミクスを使ったり、ゴールドラット氏が CCPM のシミュレーションをしたりしています。

■監査(Check)

進捗なり実績を入れたならば、チェックしなければ意味がありません。逆に言えば、チェックする必要の無い値ならば、観察する必要はありません、というのがデマルコの言葉です。

なので、実績に従って、進捗率を計算します。EVM でも良いし、工事進行基準でも良いし、バーンダウンチャートでも良いし、よく言われる「見える化」ってのは、ここのことですね。

■見直し(Action)

世の中のプロジェクトで一番できていないのが、この見直し/再計画です。
最初の計画からずれていてもそのまま進めてしまったり、現実と離れているにもかかわらず計画表を直さなかったり…と言いますか、計画/配員表のメンテナンスそのものに労力が掛かり過ぎるために、計画を見直さないという状態がほとんどです。現状と乖離しているわけですね。

なので、VPL(Verifiable Plan Language)では、この部分を厚く保護していきます。

・タスクの追加や削除
・配員(リソース)を調節
・スケジュール(プロジェクト期間、マイルストーン)を調節

が「手軽に」できるようにします。まぁ、少なくとも計画(Plan)と同じ程度の手間でできるようにします。

さて、これを具体的にどうするのかというのを落とし込みますか。

カテゴリー: プロジェクト管理, Plan Language | verifiable plan language の目指すところを考察してみる はコメントを受け付けていません

F#でER図を書こうと思ったけど止めておくの巻

#light

let (||-||) x y =
    printfn &quot;%A join %A: 1 vs 1&quot;
let (>|-||) x y =
    printfn &quot;%A join %A: 1..* vs 1&quot;
let (>@-||) x y =
    printfn &quot;%A join %A: 0..* vs 1&quot;
let (|@-||) x y =
    printfn &quot;%A join %A: 0..1 vs 1&quot;

F# ではオペレータの再定義が自由なので、色々な演算子が作れる…ってことは、ER図の鳥足みたいのもできるのでは?と思ったのがこれ。

演算子としては異様、と言いますか単純に外部結合をするだけのために使う、のかな。

Products.category_id >@-@| Categories.id

のような感じで、Products テーブルと Categories テーブルが「多対1」のように示すわけですが、思いついてはみたけれど何のために使うのかわかりません。O/R マッピングを XML ファイルで定義するよりも使い手があれば、それでもいいんですが。

そういえば、xml 形式の定義で思い出したのですが、設定ファイルというのは何も xml 形式やバイナリ形式にする必要もないのです。設定ファイルというと、昔は ini ファイル形式だったり、適当なバイナリ形式だったり、テキスト形式だったりして、最近は xml 形式が流行りな訳ですが、コード自身をファイルに保存しておくという形式があります。

TeX 形式や postscript 形式がそうで、実はコードとデータを兼ねた形式になります。あと、LISP もコードとデータを混在させることが可能です。

と言葉で書いても良くわからないので、具体例を出してみます。

<persons>
	<person>
		<name>masuda tomoaki</name>
		<addr>tokyo</addr>
		<telephone>03-0000-0000</telephone>
	</person>	
</persons>

このような XML 形式があるとします。これを INI ファイル形式で書くとすると、一例としてこんな風に書けます。

[persons]
name="masuda"
[person.masuda]
addr="tokyo"
telephone="03-000-0000"

あと LISP で書くとこんな感じです。

(persons
 (person 
  (name "masuda")
  (addr "tokyo")
  (telehphone "03-0000-0000")))

# 本当はクォートしないといけないような気がするのですが、まあ、こんな感じです。
実は、XML 形式と LISP 形式はトポロジー的に似ています。閉じ括弧/タグが必要という点で同じですね。

今度は TeX 風に書いてみると、

\begin{persons}
 \begin{person}
  \set name="masuda"
  \set addr="tokyo"
  \set telephone 
 \end{person}
\end{persons}

な感じです。\set のような書き方があったハズですが、覚えていません。
さて、肝心の C++ のコード自体でデータ構造を表そうとすると次のように書けます。

Persons.Add( 
 new Person( 
  new Name("masuda"),
  new Addr("Tokyo"),
  new Telephone("03-0000-0000")));

XML 形式や LISP 形式に似ています。これならば、XML 形式にしたほう楽だろうと思うかもしれませんが(いや、それは事実なんですけど)、このように C++ のコードにしておくと、コードの中にデータを埋め込めるという利点があります。
このデータ構造を data.txt というファイルに記述しておき include します。

void func() 
{
#include "data.txt";

	// Persons に対して操作をする
}

こんな風にすると、データの差し替えが手軽にできます。今で云えば、JSON の埋め込み型の要領です。
実はこの技、データだけを他の処理系(Excelなど)から生成して、C++ で取り込むということができます。C# や VB だとこれが使えないんですよね…ちょっと不便かなぁと。

カテゴリー: 雑談 | F#でER図を書こうと思ったけど止めておくの巻 はコメントを受け付けていません

久し振りにアナリシスパターンを紐解いて計画パターンを見直す

Verifiable Plan Language(検証可能な計画言語の思索)
http://plan-language.com/

ってことで、【ネタ的に】ドメインを取って、ひとまず wordpress を立てました。

言語ってことになっていますが、UML のように記号化のところに重きを置くのではなくて、【検証可能な】というのが主題です。なので、プログラム言語自体は、F# でも C# でも何でもいいのです(ってのが目標)。当面は、私の知っている言語が対象になるので、C#, F#, Java, PHP ですねぇ。スクリプト系の言語として、Python か Ruby なんですが。多分、Python になると思います。

さて、Verifable Plan Language 略して VPL は、計画段階からプロジェクト/プロダクトのプロセスの実行までを言語化します…って、こういうと自分でも何を言っているか分からないのですがw、PMBOK の Planning プロセスを言語化するというのが正しいのかなぁ。WBS なり、Gantt Chart なり、IDEF0 や DFD なりと planning から design process にながっる手法はいくつかあるのですが、やっぱり「検証できない」というのが致命的だと思っています。

そう、その計画は「妥当なのか?」というの妥当性は、プロジェクトの最後にならないと分からないとう「不確実性」を抱えたままなのです。勿論、プロジェクトの初期段階では、不確実性が多いのは確かなのですが、プロジェクトの終わりになっても「同じ計画を抱えたまま」では不確実性が多いままになってしまうのが問題です。現実問題として、明日リリースできるものが手元にあるのであれば、不確実性は限りなく 0 に近いと言えるわけです(勿論、不慮の事故というのもあるんですが)。

で、この手の論証なんかは、plan-language.com のほうでやるとして(moonmile.net/blog のほうは、もっと現場に接した「技術」的なところのおすそ分けなので)、ここでは、UIDD 的に WBS を作成するプロセスをプログラミングします。って、言っても分かりづらいので、実例を。

namespace PlanLanguage.Test
{
	public class TestGanttChart
	{
		public void MakeWBS()
		{
			// プロジェクトを作成する
			var proj = new WBSProject(&quot;WBSプロジェクトの作成&quot;);
			
			// トップダウンでWBSを作成する
			proj.CreateWBS(&quot;100&quot;, &quot;要件定義&quot;);
			proj.CreateWBS(&quot;200&quot;, &quot;設計工程&quot;);
			proj.CreateWBS(&quot;300&quot;, &quot;実装工程&quot;);
			proj.CreateWBS(&quot;400&quot;, &quot;結合試験&quot;);
			proj.CreateWBS(&quot;500&quot;, &quot;システム試験&quot;);
			proj.CreateWBS(&quot;600&quot;, &quot;試験運用&quot;);

			// ブレークダウン
			proj.WBS[&quot;100&quot;].BreakDown(&quot;101&quot;, &quot;機能要件&quot;);
			proj.WBS[&quot;100&quot;].BreakDown(&quot;102&quot;, &quot;性能要件&quot;);
			proj.WBS[&quot;100&quot;].BreakDown(&quot;103&quot;, &quot;開発期間&quot;);
			proj.WBS[&quot;100&quot;].BreakDown(&quot;104&quot;, &quot;開発規模(金額)&quot;);
			proj.WBS[&quot;100&quot;].BreakDown(&quot;105&quot;, &quot;開発規模(人員)&quot;);
			proj.WBS[&quot;100&quot;].BreakDown(&quot;106&quot;, &quot;システム概要設計&quot;);
			proj.WBS[&quot;100&quot;].BreakDown(&quot;107&quot;, &quot;既存システム解析&quot;);

			// PERT情報(期間)を設定
			proj.WBS[&quot;101&quot;].Period = 10;
			proj.WBS[&quot;102&quot;].Period = 10;
			proj.WBS[&quot;103&quot;].Period = 5;
			proj.WBS[&quot;104&quot;].Period = 5;
			// PERT情報(前後関係)を設定
			proj.TopWBS.NextTask = proj.WBS[&quot;101&quot;];
			proj.WBS[&quot;101&quot;].NextTask = proj.WBS[&quot;102&quot;]; // 並行作業
			proj.WBS[&quot;101&quot;].NextTask = proj.WBS[&quot;103&quot;]; // 
			proj.WBS[&quot;102&quot;].NextTask = proj.WBS[&quot;104&quot;]; // 合流
			proj.WBS[&quot;103&quot;].NextTask = proj.WBS[&quot;104&quot;]; 
			proj.WBS[&quot;104&quot;].NextTask = proj.WBS[&quot;105&quot;];

			// マイルストーンの設定
			proj.TopWBS.MileStone = new DateTime(2011, 4, 1);

			// リソースを設定
			proj.AddStaff(&quot;masuda&quot;, 0.5);
			proj.AddStaff(&quot;yamada&quot;, 1.0);
			proj.AddStaff(&quot;tomoaki&quot;,1.0);
			
			// ガントチャート作成
			GanttChart gc = proj.CreateGanntChart();

			// クリティカルパスの取得
			List<WBS> cp = proj.GetCriticalPath();
		}
	}
}

WBS(Work Break down Structure)の作り方は、各社まちまちなのでここでは解説しませんが、大まかに言えば「トップダウン」で作ります。トップにプロジェクト自身があって、下へ下へとツリー状になっていきます。WBS の良いところは(PMBOK で言われていることは)、各タスクが重複しないところです。トップダウンで分類がされているので、下層にあるタスクは重複しません。
# と同時に、「重複できない」という制限があるのですが。これは論理学的にという意味で。

手順としては、

1. 大項目の WBS を出す。
2. WBS を作業単位になるまでブレークダウンする。
3. PERT 図を作成するための、期間(period)と前後関係(relation)を設定する。
4. Gantt Chart にするために、時間軸(マイルストーンなど)を設定する。
5. リソース(人,場所,機械など)を設定
6. ガントチャートを作成

という流れになります。

# コードのほうは、Project に直接挿入しているけど、本来は Plan ですよね。
# これは後から修正。

で、ここまでは計画段階なので、この計画を基にして実行プロセスを動かします。
PMBOK では、実行プロセスは非常に薄い(進捗管理程度しかない)のですが、現場のプロジェクトとしては、

・各タスクの進捗率
・各タスクの遅延状況
・タスクの削除/追加
・再計画/スケジューリング

が必須になります。あと、人員が減ることもある(俗に言う、ウエディングリスク)ので、これを実行プロセスを言語化します。

もうひとつ、CCPM 的に、タスクの遅延確率(あるいはプロジェクトバッファ)を計算する必要があるので、全体スケジュールに影響を及ぼす可能性のあるタスク(合流タスク、あるいは障壁となるタスク)の監視を行います。

こっちの具体例はのちほど。

カテゴリー: UIDD, プロジェクト管理, Plan Language | 久し振りにアナリシスパターンを紐解いて計画パターンを見直す はコメントを受け付けていません

Planning Lanugage 事始め

CakePHP の発想から、EXDoc, DBUnit のひな型を作ってきたのですが、そうか、UIDD に則ればガントチャートのような UI は冗長なのですね。もっと MVC 的に Logic 部分(Controller部分) を分離させても良いわけです。

ガントチャート – Wikipedia
http://ja.wikipedia.org/wiki/%E3%82%AC%E3%83%B3%E3%83%88%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88

ブレークダウンした WBS からスケジュールに落とす時は、ガントチャートを使うことが多いのですが、本来は PERT を考慮しないと駄目です…と言いますか、最近のガントチャートツールは、このあたりを押さえている(と思う)ので問題ははないんのですが、MS Project をはじめとして、この手のツールは、

  • 高価すぎる
  • チャートを作るのが非常に手間
  • 変化に弱すぎる

のが問題です。高価すぎるところは、Excel でスケジューラーを作ったり、フリーのものを活用したりすれば良いのですが、2番目と3番目が致命的です。

WBS でもストリーカードでも良いのですが、それぞれを「タスク」あるいは「ワーク」という単位に落とし込んだ後、それをどの順番で行うのか、を組み立てるのに非常に手間が掛かります。実は、この手間は、

  • タスクの連結を、タスクを出した後に行ってしまう。
  • タスクの連結を知っているのは、開発者のみだったりする。

という理由から来ています。タスク同士の連結は、PERT 図を書いている方は分かると思いますが、前のタスクがあり後ろのタスクがあるという「前提条件」が必ずあります。これは工場で使われるプロダクトの「製造工程」と同じことで、前の製品/部品ができていなければ、後ろの工程が進みません。そのあたりをタスクを動かしても、単純に待ちが発生してしまうだけです。

で、タスクに落とすのは誰がやるかというと、マネージャがやることが多いのです。なおで、ブレークダウンしたタスクをさて、タスクの連結にするときに止まってしまいます。あるいは、シーケンシャルに並べることによって全体の工数を算出します。
この工数の算出の仕方は、「やや悪い」程度で決して「悪く」はないのですが、「良い」わけでもありません。
前後のタスクにより制約が発生しているのであれば、その制約を取り除くことで効率化をするという「制約理論」の手法を導入することで、これを「良い」方向に動かすことができます。

CCPM やクリティカルチェーン、工場の製造工程の場合には、このタスクの連結を実施者自らが考えます。と言いますか、工程のラインをどのように組み合わせるのかという専門家がデザインをします(勿論、マネージャが、工程の専門家になるのがベストなのですが、マネージャの役割として「マネジメントとしての管理者」と「プロジェクトの計画者」と「実施時の監督官」という 3 重の責務を負わせるのは難しいと思われます)。

いわゆる、「建築家」と「現場監督」の違いがある、と覚えておけばよいでしょう。

なので、現場を知る人がこのタスクの連結を知っていますという矛盾が発生しています。アジャイル開発(XP やスクラムなど)は、この責務を意図的に混沌とさせることで回避する方法です。

と、前置きが長くなりすぎましたが…ええと、話を最初に戻すと、ガントチャートを作るのに何も豊富な GUI は必要ないのです。

  • タスク自身、タスク間のデータを保持する。
  • タスク間の連結が変更しやすい。
  • スケジューリングを自動化する。
  • 最後に GUI で確認する。

というポイントを抑えておけば、ガントチャートが作成できます。と言いますか、最初の2点を抑えておけば、ガントチャートを作ることは可能なんですね。

C# や VB では、言語的な制約が多いので、いずれ F# に直す予定ですが、ひとまず UIDD 的にコマンドラインで書いてみる。


// タスクを設定
PlanA.Tasks.Add( new Task("001","最初のタスク", 2 days ));
PlanA.Tasks.Add( new Task("002","次のタスク", 3 days ));
PlanA.Tasks.Add( new Task("003","並列タスク", 2 days ));
PlanA.Tasks.Add( new Task("004","最後のタスク", 3 days ));

// 連携
PlanA.Tasks["001"].Adter = PlanA.Tasks["002"];
PlanA.Tasks["001"].Adter = PlanA.Tasks["003"];
PlanA.Tasks["002"].Adter = PlanA.Tasks["004"];
PlanA.Tasks["003"].Adter = PlanA.Tasks["004"];

// マイルストーンを設定
PlanA.Task["001"].MileStone = #2011-04-01# ;

// XML 形式で出力
PlanA.XML ;

// GUI で出力
PlanA.GUI ;

// 同じタスクで Plan B を作成する
PlanB.Tasks = PlanA.Tasks ;

// 連携を変更
PlanB.Tasks["001"].Adter = PlanB.Tasks["002"];
PlanB.Tasks["002"].Adter = PlanB.Tasks["003"];
PlanB.Tasks["003"].Adter = PlanB.Tasks["004"];

// クリティカルチェーンを取得
PlanB.CriticalChain ;

// クリティカルチェーンタスクを取得
PlanB.CriticalChain.Tasks

これは静的コンパイルをしないといけないので、F# のインタプリターを使うか、PowerShell を使います。
ま、これの実装は後ほど。

カテゴリー: 設計, UIDD, プロジェクト管理 | Planning Lanugage 事始め はコメントを受け付けていません

DBUnit の .NET 版を作成する(準備)

実は java 版の DBUnit は xml からデータを読みに行くし、DBUnit.net は動作的に同じなので、自前の DBUnit を作ります。
名前がダブっているけど、これは後から。

UIDD に則って、最初はテストケースをざっくりと作ります。
こんな風に使えると、マニュアルなしでも使えるかもね、という雰囲気で。

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using NUnit.Framework;
using DBUnit.Framework;

namespace Test.DBUnit
{
	[TestFixture]
	public class TestDBUnit
	{
        /// <summary>
        /// 前処理
        /// </summary>
		[SetUp]
		public void SetUp()
		{
            // コネクションを保持
			DB.Connection = new SqlConnection(&quot;&quot;);
		}

        /// <summary>
        /// 後処理
        /// </summary>
        [TearDown]
        public void TearDown()
        {
            // テーブルの内容を削除
            DB.Clear();
        }

        /// <summary>
        /// DataTable を使ってテスト
        /// </summary>
		[TestCase]
		public void TestSetUp()
		{
			DataTable dt = new DataTable();

			dt.TableName = &quot;XProduct&quot;;
			dt.Columns.Add( new DataColumn(&quot;ID&quot;, typeof(int) ));
			dt.Columns.Add( new DataColumn(&quot;Name&quot;, typeof(string) ));
			DataRow dr = dt.NewRow();
			dr[&quot;ID&quot;] = 10;
			dr[&quot;Name&quot;] = &quot;masuda&quot;;
            dt.Rows.Add(dr);

            dr = dt.NewRow();
            dr[&quot;ID&quot;] = 20;
            dr[&quot;Name&quot;] = &quot;tomoaki&quot;;
            dt.Rows.Add(dr);

			// 前処理
			DB.Insert(dt);

            DataTable result = new DataTable();
            SqlDataAdapter da = new SqlDataAdapter(
                &quot;SELECT * FROM XProduct&quot;, DB.Connection);
            da.Fill(result);

            Assert.AreEqual(2, result.Rows.Count);
            Assert.AreEqual(10, result.Rows[0][&quot;ID&quot;]);
            Assert.AreEqual(&quot;masuda&quot;, result.Rows[0][&quot;Name&quot;]);

            // 後処理
			DB.Truncate(dt);
		}

        /// <summary>
        /// 独自のDBUnit.DBTable を使ってテスト
        /// </summary>
		[TestCase]
		public void TestSetup2()
		{
			DBTable dt = new DBTable(&quot;XProduct&quot;);

            // デフォルト値を設定
			dt.SetDefault(
                &quot;ID&quot;, 0, 
                &quot;Name&quot;, &quot;&quot;, 
                &quot;Update_at&quot;, DateTime.Now);
            // 設定する列名
			dt.AddColumns(&quot;ID&quot;,&quot;Name&quot;);
            // 行を設定
			dt.AddRow(10, &quot;masuda&quot;);
			dt.AddRow(20, &quot;tomoaki&quot;);
            // insert を実行
			dt.Insert();

            // データをクリア
			dt.Clear();
			Assert.AreEqual( 0, dt.Rows.Count );

            // データベースから読み込み
			dt.Select();
			Assert.AreEqual(2, dt.Rows.Count);
			Assert.AreEqual(10, dt[0][&quot;ID&quot;]);
			Assert.AreEqual(&quot;masuda&quot;, dt[0][&quot;Name&quot;]);

            // テーブルの内容を削除
			dt.Delete();
		}
	}
}

微妙にコードが冗長なのは、.NET v2.0 を対象にしているからです。手元のプロジェクトが v2.0 なのでそれに合わせて作らないと意味がないので、というのが第一の理由。LINQ to SQL とタプルなんかを使えるともう少しマシなコードになると思うのですが、そこは v2.0 がターゲットということで。

TestSetUp のほうでは、既存の DataTable/DataRow を使ってみたパターンです。カラムを作るところがやや面倒なのですが、これだとデフォルト値を入れるのが大変なので、テストケースを作るのが苦労しそうです。

なので、TestSetup2 のように、べたべたな仕様にするのがいいかなと。
所詮 NUnit との組み合わせでテストケースにしか使わないので、分かりやすいほうが良いでしょう。型付の DataTable を作成することも考えたのですが、これは環境依存(というかプロジェクトの方針依存)になりそうなので、DataTable 風に扱います。

これをざっくりとクラスとメソッドを作って、コンパイルが通るようにしたのが下記です。

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;


namespace DBUnit.Framework
{
	public class DB
	{
        public static SqlConnection Connection;
		public static void Insert(DataTable dt)
		{
		}
		public static void Delete(DataTable dt)
		{
		}
		public static void Truncate(DataTable dt)
		{
		}
        public static void Clear()
        {
        }
	}

	public class DBRow
	{
		public int Count;
		public object this[string key]
		{
			get
			{
				return null;
			}
		}
	}
	public class DBTable
	{
		public string TableName;
		public List<DBRow> Rows;

        public DBTable(string name)
        {
            this.TableName = name;
        }

		public void SetDefault(params object[] ps)
		{
		}
		public void AddColumns(params string[] columns)
		{
		}
		public void AddRow(params object[] rows)
		{
		}
		public void Insert()
		{
		}
		public void Clear()
		{
		}
		public void Select()
		{
		}
		public void Delete()
		{
		}
		public DBRow this[ int index ] 
		{
			get
			{
				return this.Rows[index];
			}
		}
	}
}

そして、DBUnit を NUnit を使って作成する、という不思議/当たり前なことをやっていきます。

カテゴリー: C#, xUnit | DBUnit の .NET 版を作成する(準備) はコメントを受け付けていません

xUnit を使うと、どこまで効率的になるのか?

xUnit を導入する積極的な理由をメモ書き。

Windows アプリケーションでは NUnit を使える構造設計を
http://www.moonmile.net/blog/archives/2054

導入できるかどうかは上記のポイントがありますが、さて、xUnit を導入する積極的な理由はどこにあるのでしょうか?
…と言いますか、いままで何とか仕事をこなしてきたから、同じ方法でやりたいのは当然なんですよね。むやみに Change !!! もできないわけで、そこには実装者なり経営者なりのメリットが必要なわけです。

win-win な関係って奴ですね。相手に対する営業的トークも少し混じりますがw

■現場でのメリット

昔、Eclipse が無かったころは、Java のコーディングをするときには、vi/emacs で十分だよ、という意見が大半でした。Visual Basic 2.0 の頃は、統合開発環境というよりは、その言語に【密着した】コーディング環境というイメージが強いのです。
でも、今 Eclipse を使わずに Java のコーディングをすることは皆無…とまでは言いませんが、少ないですよね。PHP のコーディングでも、JavaScript のコーディングでも、それなりの IDE(統合開発環境)を使うのが普通になっています。
で、xUnit を使うというのは、それくらい【便利】なのです。

これは、以前、携帯電話の中身(アプリケーションじゃなくて、内部実装を C言語で)を作っていた頃なのですが、その頃 xUnit の導入は「導入コストがかかる」という意見が大半です。まぁ、この「導入コスト」については、

・xUnit を学習するための暇がプロジェクトで取れない。
・xUnit を作っても、単体テスト用の Excel シートを書くと二重に手間がかかる。
・現在、単体テストがうまくいっているから、xUnit を導入するメリットはない。

のように、学習コストが大きいことや、導入したときのメリット(現場レベルのメリット)がないことを理由に避けられます。

このパターンは、なにも xUnit の導入に限ったことではなく、

・VB6.0 のアプリを VB 2010 に移行する
・データベースの扱い方を、SQL 文を学習せずに、古い O/R マッピングを使い続ける。
・統合開発環境を導入しない

というような【新しいこと】に対する拒否反応…と言いますか、現状の慣性の法則が働くので、特に珍しいことではありません。心理的な障壁が多いのです。
いわゆる、新しいものが好きな開発者は、どんどん導入するのですが(かといって、新しいものを導入しすぎてあらぬ方向に行きかねませんが)、いわゆる大半の開発者はそのままなのです。

なので、

・新しいものを導入する前の、心理的な不安
・新しいものを導入した後の、メリット

を比較して、心理的な不安 < メリット となるようにします。
「心理的な」という言葉を使っているのは、過去の業務コストやら時間やらを計算してたたき出しても、「心理的な不安」は減らないからです。このあたりは、心理学を少しかじるとわかります。

さて、心理的な不安を低くする方策としては、

・学習コストが、【思ったよりも】低いことを強調する。
 → 数時間の xUnit の学習だけで済む。
 → 基本は、Assert.AreEquals だけ。
 → テストの基本パターンをあらかじめ用意する。
・単体テストの Excel 試験仕様書の記述が軽くなることを強調する。
 → 二度手間はやらない。
 → ざっと分類書くだけにする。
 → 単体テスト自体は、コードや、それに書かれたコメントで十分

を強調します。

・現在の単体テストがうまくいっているから

の理由のほうは、放置しておきます。

# そういう場合は、放置しておいたほうが「うまくいく」し、
# 逆にうまくいかない場合は、「関わらない」ほうが無難です。
# やむを得ず、関わらなければいけない場合は、トップダウンで適用します。

導入後のメリットのほうは、

・再帰試験がやりやすくなる。
 → 実際に xUnit のコードを実プロジェクト用に書いて、動かすとよいでしょう。
・複雑なロジックの完成が非常に早くなる。
 → いままで1日かかっていたコーディングが1時間で終わることも稀ではありません。
 → 慣れてくると適当なテストコードを書いても、それなりにロジックが作れるのです。

を強調し、実践します。実際に動かしてみないとメリットは分かりづらいですからね。

MVC パターンを適用していれば、他人が書いたコードに対して、xUnit を導入することもできます。
このあたり、もっと慣れてくれば、詳細設計書自体を半減させることも可能です。

■経営サイドのメリット

技術的なことは分からないけど、お金のこと(コストのこと)が気になるマネージャや経営者への説明は、技術者への説明とは違って「コストが下がる」「儲かる」ことを強調します。
まぁ、それが「経営」ってものですから。

・xUnit を導入すると、次回のプロジェクトで、単体試験のコストが半減します。
・xUnit を導入すると、プログラマを 2 割減らしても大丈夫です。
・xUnit を導入すると、詳細設計から結合設計までの期間を 2 割減らしても大丈夫です。
・xUnit を導入すると、顧客からの要求を取り入れやすくなります。
・xUnit を導入すると、不具合が半減して、品質が非常によくなります。

多少「売り」文句が入っていますが、真面目に導入するとこのくらいの削減ができます。
実は、削減≒プログラマの削減になるので、開発者にとっては痛し痒しなわけですが、他社にとっての競争力になることは確かです。「品質」がよく、「早期納入」が可能になります。

# CCPM と合わせると、「納期遅延」にも対応が可能です。

xUnit は Visual Stuido などの製品とは違って、購入コストは 0 ですから(大抵は無償で配布されています)、スタートアップでコスト的に躓くことはありません。

XP や TDD を導入すれば更に加速がつきますが、最初は xUnit の導入だけで十分です。xUnit を導入するときの【文化】として適切でない場合もあるので、そのときは従来の開発スタイルに戻すのも良いです。
適合不適合は、どこにもあるもので、それぞれの文化にあわなければ導入を見合わせたほうがよいでしょう。

カテゴリー: 仕事, xUnit | xUnit を使うと、どこまで効率的になるのか? はコメントを受け付けていません

計画停電でもできるIT屋さんのお仕事法

どうやら、計画停電(輪番停電)が4月末まで続きそうな勢いなので、IT 屋さんとしてはえーッ!!! 一日3時間も停電していたら仕事にならないよッ!!!な状態にならないように、準備をしておきます、の巻。

節電に関しては、使っていないディスプレイ消す、ノートブックで仕事をする、サーバーを落とす、など直接的な方法がありますが、私のほうは設計/実装段階で使える節電ノウハウをお伝えします。

手っ取り早く云えば、XP の40時間勤務を実践するノウハウなんですけどね。

■ペーパープロトタイピングを応用

本式なところは↓な訳ですが、

Amazon.co.jp: ペーパープロトタイピング 最適なユーザインタフェースを効率よくデザインする: Carolyn Snyder, 黒須 正明: 本
http://www.amazon.co.jp/dp/4274065669
今でも役立つ、ペーパープロトタイピングのススメ – Feel Like A Fallinstar
http://www.fallinstar.org/ui/post_104.html

もうちょっと節電できるように範囲を広げます。
例えば、UML 設計とか、データベース設計をしているときにディスプレイと睨めっこをしているのを止めます。一旦、パソコンの電源を落として、紙にプロトタイピングをします。

これ、単なる節電の効果だけじゃなくて、ディスプレイを付けているとメールやらツイッターやらインターネットやらが気になってしまって、他のことをやってしまうので、集中するという点で実に重要です。余計な情報を入れない、気を散らさないという形で紙に書いて、後からオーサリングツールに清書します。

当然、仕様書なんかが電子化されているときはパソコンを見ながらということになってしまうのですが、一部分だけ印刷をしたりしてしのぎます。iPad などのタブレットタイプやノートブックを利用しても効果があがります。要は、メールなどの余計な情報を入れない、見れない状態にすると良いという話です。

■PSP(パーソナル・ソフトウェア・プロセス)を活用する

PSP と言ってもプレイステーションのアレではなくて、パーソナル・ソフトウェア・プロセスの略です。

Amazon.co.jp: パーソナルソフトウェアプロセス入門: ワッツ ハンフリー, Watts S. Humphrey, PSPネットワーク: 本
http://www.amazon.co.jp/dp/4320120132
パーソナルソフトウェアプロセス(PSP)実践講座 ~第1日 自分の生産性・品質を算出する(1/5):CodeZine
http://codezine.jp/article/detail/3937

厳しい開発プロセスですが、時間制限があるときに非常に有効です。簡単なやり方としては、

  1. 最初に予想時間を立てる(1時間程度で)
  2. 実行して、実測値を計測する。
  3. 実測値に合わせて、予想時間を見直す。

という細かい単位で計画を見直します。いわゆる PDCA を短時間(1時間単位)で廻すという感じですね。

仕事/作業に対して集中力を要するので、だらだらした仕事振りができなくなります。ある意味で、非常に疲れるので短期間しかできないし、1日8時間弱が限界です。
ですが、集中して仕事ができるので、効率がよくなります。

停電する前に集中して作業をこなさなければならないときには、この方法は非常に有効です。

■付箋/ストーリーカードを活用する

パソコンで Excel などを使って ToDo 管理をしている場合は、一時的に付箋に書きつけるとよいです。というか、この手の使い捨ての ToDo なんかは付箋やストーリーカードに書きつけるのが、次々と仕事をこなす時に便利です。

付箋やストーリーカードについては、予定時間や完了予定日を書き込んでおきます。そして、仕事が終わったら外して捨ててしまいます。
この捨てるという行為が、結構すがすがしく、次々と仕事をしている雰囲気を生み出してくれます。

付箋に関しては、半日以内でできる仕事を書き込みます。
このリアルな感覚は、ディスプレイから離れて仕事をするときに非常に良いです。

■CCPM/GTD を活用する

CCPM(クリティカル・チェーン・プロジェクト・マネージメント)や GTD(Getting Things Done)を利用します。

Getting Things Done – Wikipedia
http://ja.wikipedia.org/wiki/Getting_Things_Done
誠 Biz.ID:Getting Things Done(GTD)まとめ
http://bizmakoto.jp/bizid/gtd_index.html

GTD に関しては、オンラインツールが多くあるのですが、最初は紙のノートに書き出したほうがよいです。思いつく限り、ボールペンで書き出すと、キーボードで打ち込むのとは違った感覚で書き出せます。

CCPM も GTD も、優先順位を付けて、優先度の高いもの、期限が近いものからやっていくというのが同じ方法です。優先度が高いものをこなすと、優先度が低いものはやらなくても良いという状態になることがあります。それが、CCPM, GTD の狙いです。

CCPM のタスク抽出や連携は、パソコン上でやるよりも大きな紙の上に付箋を貼りながらやったほうが見通しが良くなります。なので、これはオフラインの仕事に向いています。

■技術書を読もう

この際ですから、節電関係で余った時間を技術書を読むことに使っては如何でしょうか?地震の情報はインターネットで最新情報を収集することができるように
なりましたが、情報におぼれているだけで、何もできなくなっては仕方がありません。。。と言いますか、情報が過多になると人間の頭は働かなくなってしまいます。コンピュータ用語で云えば、スレッドが多くなりすぎて、本来のタスクに使う CPU が食いつぶされてしまうという状態ですね。なので、地震情報を得るのは、別途ラジオなり、定期的にテレビなりで確認するようにして、本来の自分の仕事に専念することができる状態を作ります。

阪神大震災の時には、私は大阪にいて午前4時にパソコンを弄っていました。六畳一間の下宿で、2 つの本棚が倒れたのですが、幸い寝ていなかったため(苦笑)本棚の下敷きになることは避けられました。

その後のテレビには、ちょっとばかし怒りを覚えたわけですが、本棚の本を片付けて本の整理をしてズレた机を直して(30センチ以上ずれていました)、1日経った後に日常的に大学に行きました。
停電、断水を経験したのですが、できるだけ早く日常の感覚を取り戻すことが第一です。
その上で、ボランティアなり募金なり物資支援なりをしてくださると良いかと思います。

~~

と、いちソフトウェア開発者としてできることはここまでです。
津波後の処理、東京の列車の復旧、福島原発(私は原子力学科卒なので、一般の人よりは詳細がわかります)など、問題はたくさんありますが、非難/批判よりもまずは、一歩ずつ進むことを大切にしては如何でしょうか?

カテゴリー: 開発, 設計, プロジェクト管理, 仕事, 雑談 | 計画停電でもできるIT屋さんのお仕事法 はコメントを受け付けていません

windows で objective-c を動かす

GNUstepでobjective-cを学ぶ(1) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/595

のところで環境構築をするわけですが、

GNUstep Windows Installer
http://www.gnustep.org/experience/Windows.html

からダウンロードするのは、

  • GNUstep MSYS System
  • GNUstep Core
  • GNUstep Devel

の3つです。

最初の2つは、GNUStep を動かすために必要で、最後のGNUStep Develに gcc が入っています。

そんな訳で、objective-c の xUnit は何処だ?

OCUnit
http://www.sente.ch/software/ocunit/

Mac OS X 版しかないから、ソースからコンパイル?何故か、windows からダウンロードすると途中で駄目になるので mac からやらないと駄目か?

カテゴリー: 開発, 雑談, Objective-C | windows で objective-c を動かす はコメントを受け付けていません