ちょっと厨二っぽいSEのブログ

プログラミングとかのシステム備忘録など

【プロジェクトマネジメント】見積もりについて考えてみましょう【PMBOK】

■見積もりの種類

一言で見積もりといっても、色々あります。
まず、「何のための見積もりか」について考えてみましょう。

例えば以下のように、見積もりを提出する先によって見積もりの意味が変わってきます。

1.自チームのPMが見積もった、そのプロジェクトに実際にかかるコスト
2.自チームのPMが見積もった、ある程度のバグフィックス(バッファ)を加味した現実的なコスト
3.自チームのPMが見積もった、クライアントに報告するための少しゆとりのあるコスト
4.お客様ベンダーに対して報告するための見積もり

たとえば4の場合、競合他社に勝つために実際よりも少ないコストを報告する場合があります。
いわゆるコンペの場合などがそれにあたり、実際に見積もりコストとかけ離れる場合があります。

と、このように
「見積もり」と言ったらどの見積もりを指すのかを把握しておかないと、後々のノウハウになりません。


■見積もり粒度には2種類ある

PMBOK的には、見積もりは以下の種類があります。

概算見積もり
確定見積もり


概算見積もりは読んで字のごとく、ざっくりとした見積もりです。
このような不確実な見積もりでも、ざっくりとした予定が立てられたり、投資の妥当性(そのプロジェクトをやる意味があるのか)などがわかります。

確定見積もりは、実際にプロジェクトを行う時に必要な、最終的に確定した見積もりのことです。

■見積もりの不確実性

見積もりは不確実なものです。
プロジェクトの大きさにもよりますが、初期段階の見積もりは実際のコストに対して0.25倍~4倍程度の誤差が生まれます。

多くの場合、納期の見積もりは過小見積もりである場合が多く
開発者自身が見積もりを行う場合においてその傾向が強いと言われています。

認知バイアスとは

例えばあなたが以下のような質問をされたとします。

「だいたい5日くらいで作れる?」
「だいたい100万円くらいで作れる?」

このような質問をされると最初に出された数字(アンカー)に近づくバイアスがかかってしまいます。

その数字で実現しようとする力が働き、過小見積もりをしてしまうのです。

このことを「アンカリング」と呼びます。

このような間違った見積もりを避けるための手法として、
チームメンバーでバラバラに見積もる方法があります。

■よく使われる見積もり手法

以下のように、タスクや要件の難易度をポイント化(重み付け)して見積もる方法が主流です。

ファンクションポイント法

それぞれのタスクを難易度によってポイント付けをし、それを集計して見積もる。
また、ユーザーから見た機能を5種類に分類して集計する。
f:id:ryokwkm:20160725010354p:plain

それぞれ
内部論理ファイル(ILF):アプリケーション内部にあるデータ
外部インターフェースファイル(EIF):アプリケーション外にあるデータ
外部入力(EI):アプリケーションの外部からデータや命令を受け取って行う処理
外部出力(EO):アプリケーション外部にデータを出力
外部紹介(EQ):アプリケーション外部に対して、データ照会(検索など)結果を出力する処理

このようにあらかじめ定義しておくことで、恣意性が入らず制度の高い見積もりが多いという調査結果があります。

ユースケースポイント法

システムのユースケースを複雑度に応じてポイント化し、集計する方法

f:id:ryokwkm:20160725011420p:plain

COCOMOⅡ法

見積もったシステムの規模から、費用・納期を見積もる手法
システムの規模と工数は比例関係にないという知見から、さまざまな工数変動要因を取り込むモデルを提示している

f:id:ryokwkm:20160725011834p:plain

CoBRA

定量的なモデルと熟練者の意見を取り込むハイブリッドな見積もり方法

【Swift】iOSの通信について【GET, POST】

iosで通信処理を実装する方法です。

Androidですと、
threadを作って、そこに通信処理と、コールバック関数を無名クラスで作成したりしますよね。

ではiOSではどうなんでしょう


◆一番単純な書き方

class ViewController: UIViewController {

    override func viewDidLoad() {
        ....

        //urlを生成
        let url:NSURL = NSURL(string:"http://xxx.com")!

        //リクエストを生成
        let request:NSURLRequest  = NSURLRequest(URL: url)

        //リクエストを投げる self.getHttpはレスポンス取得後の処理
        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: self.getHttp)

    }

    //レスポンス取得後の処理
    func getHttp(response:NSURLResponse?,data:NSData?,error:NSError?){
        print( NSString(data: data!, encoding: NSUTF8StringEncoding)! );
    }

NSOperationQueue.mainQueue()部分がThread部分です。
Androidのように自分で作る必要はなく、あらかじめ用意されたキューを指定します。
この場合はメインスレッドを指定しています。


◆1つの関数内で完結させる

また上記の書き方ではコールバックを別の関数として書いていますが
Swiftの場合、無名関数で書くことが出来ます。

こんな感じ

let url:NSURL = NSURL(string:"http://xxx.com")!
let request:NSURLRequest  = NSURLRequest(URL: url)

NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(),
    completionHandler: {(response:NSURLResponse?, data:NSData?, error:NSError? ) in
        
        print(NSString(data: data!, encoding: NSUTF8StringEncoding)!)
    } )


◆POST通信

NSURLRequestの代わりに、NSMutableURLRequestを使用するとPOSTを送ることができます。

let query = "param1=1&param2=abc&param3=test"
let url:NSURL = NSURL(string:"http://xxx.com")!
let request = NSMutableURLRequest(URL: url!)

request.HTTPMethod = "POST"
request.HTTPBody = query.dataUsingEncoding(NSUTF8StringEncoding)

NSURLConnection.sendAsynchronousRequest(apiRequest, queue: NSOperationQueue.mainQueue(),
    completionHandler: {(response:NSURLResponse?, data:NSData?, error:NSError? ) in
        
        print( NSString(data: data!, encoding: NSUTF8StringEncoding)! )
    } )


以上です。
Androidに比べてスマートに書くことができますね。

【Javascript】クラスの書き方メモ【イベントハンドラ】

JSのクラスの書き方
忘れがちなので書いておきます

◆基本形

function MyClass( honyahonya ){
	this.initialize.apply(this,arguments);
}

MyClass.prototype={
	initialize:function(message) {
		this.message = message;
	},
	speak:function(){
		console.log( this.message );
	}
}

var myClass = new MyClass("こんにちは");
myClass.speak();

◆イベントをからませた時

function MyClass( honyahonya ){
	var mySelf = this;
	
	this.__speak = function() {
		mySelf.speak(this);
	}
	
	this.initialize.apply(this,arguments);
}

MyClass.prototype={
	initialize:function(message) {
		this.message = message;
		
		$("input[name='speakButton']").click( this.__speak );
	},
	speak:function(e){
		console.log( this.message );
		this.speak2();
	},
	speak2:function(){
		alert( this.message );
	},
}

イベント実行時、 this はclass自身を指さなくなり、
実行されたその要素(input[name='speakButton']")になります。

thisを使うためには、このように少し特殊な書き方になるのです。

この書き方の場合
speakのthisはクラス自身になり、イベントを発火させた要素は e に格納されています

【Swift2.0】 CSVデータによるCoreDataの初期化【MagicalRecord】

前回の記事で、CSVファイルから取り出したデータを、DBの初期データとして挿入する方法を書きました
ryokwkm2.hatenadiary.jp

ただSwiftのバージョンが1.2だったため
Swift2.0 で書き直したコードを載せておきます

gist.github.com


Swift2.0では enumerateや、splitの使い方が異なります

クラス外に書いた

extension String : CollectionType{}

を忘れないようにしましょう

【Swift2.0】 画面幅いっぱいに(均等に)ボタンを配置する方法 その2【カスタムView】

さて前回の続きです
ryokwkm2.hatenadiary.jp

このシリーズの目次

:カスタムViewを使う
1.カスタムViewのxibとクラスを用意する
2.xibとクラスを紐付ける
3.storyBoardとカスタムView紐付けて、動作確認

:画面幅いっぱいにボタンを配置する
4.xibで、実際のレイアウトを作成
5.コードを書く(画面に応じて大きさを変更させる)

:ボタンを押した時の処理を呼び出し元クラスで上書きする
6.プロトコルでデリゲートを定義


今回はAutoLayoutを用いて実際のレイアウトをしていきます

4.xibで、実際のレイアウトを作成

さて、ボタンを画面幅いっぱいにするということは
HTMLでいうところの width=100% にするということです

さらに、各ボタンの大きさもそれぞれ width=16.6% (6分の1) を指定したいのです

この指定方法がちょっと複雑なのです

なので、このSTEP4を、さらに3つのステップにわけて説明していきます


目次
STEP4.1 Viewに width=100% を設定
STEP4.2 Viewに width=50% を設定
STEP4.3 Viewに width=16.6% を指定し、ボタンを6個並べる

適当にViewを3つ置いてください(わかりやすいように色をつけてます)
f:id:ryokwkm:20151215184649p:plain

名前はそれぞれ
dummy
dummyLeft
dummyRight
とし、元から置いてあったViewの名前を
parentView
に変更しました


これらにそれぞれ以下の設定をします
parentView width:100%
dummy width:100%
dummyLeft width:50% (左から)
dummyRight width:50% (右から)

それでは具体的な方法を説明していきます

STEP4.1 Viewに width=100% を設定

以下の2つを設定します

parentView width:100%, height:100%
dummy width:100%, height : 30px

100%表示は
Editorメニュー > Pin > で画面幅いっぱいに表示させるようにすればOKです
parentViewに対して以下のように設定します
f:id:ryokwkm:20151215184651p:plain

paretView には width=100% height=100% を指定しました
dummy もwidth=100% を指定しますが、高さは固定値にします。 ここでは適当に 30くらいにしておきます

f:id:ryokwkm:20151215184655p:plain

右、左、下辺は parentView に合わせてください
Update Frames をすると以下のようになります
f:id:ryokwkm:20151215184657p:plain


STEP4.2 Viewに width=50% を設定

さて 50% 表示のやり方です

こちらは先ほど設定した
width=100% のView(dummy) の 50% の大きさ
という指定を行います


まずはdummyLeft を parentView の左下に添わせます
widthは設定せず、 heightは適当に10くらいに設定してみましょう
f:id:ryokwkm:20151216145754p:plain


するとエラーが表示されます
widthが未定ですよというエラーです
f:id:ryokwkm:20151216145817p:plain

続いてwidthを設定します
widthは、まず dummy と同じ大きさにします


dummyとdummyLeft を両方選択して、 pin から EqualWidths を設定
f:id:ryokwkm:20151216150144p:plain


するとdummyと同じく width:100% が設定されます
エラーも消えました
f:id:ryokwkm:20151216150248p:plain


さて、50%の秘訣はこの EqualWidthsです
今は dummyと同じ比率(Equal)になっていますが、この比率を 50% に変更します

設定した constaints から、今設定したEqualWidthsの設定に変更を加えます

f:id:ryokwkm:20151216150435p:plain

ユーティリティエリアのFirst Item を見ると、 dummy.width になっています
設定するのは dummyLeft.width に対してですので、これを入れ替えましょう

f:id:ryokwkm:20151216150737p:plain

その後、 Multiplier を変更します
現在 1 (100%) になっていますが、 これを0.5 に変更します

f:id:ryokwkm:20151216150841p:plain

すると 50% で表示させることができました


STEP4.3 Viewに width=16.6% を指定し、ボタンを6個並べる

さて、 4.1, 4.2 を理解したあなたは、以下の設定ができるはずです。
やってみましょう

parentView width:100%, height:100%
dummy width:100%
dummyLeft width:50% (左から)
dummyRight width:50% (右から)


できたらUIButtonを6個並べて、Titleを空にし、画像を指定しましょう
画像に関しては ImageとBackGroundがありますが、
どちらに指定しても完成後の見た目は同じです。

AutoLayout上ではBackgroundにした場合、以下のように伸びて表示されます

f:id:ryokwkm:20151216153300p:plain


その後以下のPinを指定すれば見た目は完成です
◆width
各UIButtonの大きさは dummy の 1/6 (0.1666)になるように EqualWidthsを設定

◆Height
上と下に関しては、どちらも parentView の上下に添わせて設定してください (height:100%)

◆位置
中央の2つは dummyLeft, dummyRight に沿わせるようにPinを設定

端の2つは、 parentViewの端に沿わせるように設定

端から2番めの2つは、端のボタン、もしくは中央のボタンのどちらかに沿わせるように設定します



この時、どのViewに添わせているかを確認しながら設定してください。

というのも、parentViewに添わせているつもりが別のViewに添わせていた ということがよくあります。


お疲れ様でした。

ただこれだと正常に動きません。
画面ごとにViewの大きさを変えるコードを少し書く必要があります

それはまた次回書きます

【Swift2.0】 画面幅いっぱいに(均等に)ボタンを配置する方法 その1【カスタムView】

iosで、画面幅いっぱいにボタンを配置する方法を説明します


パズドラのUIでお馴染みのこんなやつ
HTMLで作ると一瞬で作れてしまいますが、iOSではとても手こずります
f:id:ryokwkm:20151215130428p:plain



さらに今回は色んな画面で使いまわせるように
別ファイルで作成して、いろんなViewから呼び出して使用できるようにします。

画面はAutoLayoutを使用して作成していきます

ちょっと長くなりますので、3つの記事にわけます
先に流れをざっくりと。

このシリーズの目次

:カスタムViewを使う
1.カスタムViewのxibとクラスを用意する
2.xibとクラスを紐付ける
3.storyBoardとカスタムView紐付けて、動作確認

:画面幅いっぱいにボタンを配置する
4.xibで、実際のレイアウトを作成
5.コードを書く(画面に応じて大きさを変更させる)

:ボタンを押した時の処理を呼び出し元クラスで上書きする
6.プロトコルでデリゲートを定義

:カスタムViewを使う

色々なViewControllerから使いたいUIViewは、カスタムViewという別ファイルで作成する形式にすると便利です
まずはカスタムViewの作成方法と、その動作確認を行っていきます

1.カスタムViewのxibとクラスを用意する

xibファイルとそのクラスを作成します

xibの作成は、new fileから...
f:id:ryokwkm:20151215122733p:plain

クラスも同様に作成します

今回は以下の名前で作成しました
ToolBarView.xib
ToolBarView.swift


xibではAutoLayoutを使用して画面を設計できます
まずは動作確認のため、色のついてViewを置いただけのものを作成しましょう

初期設定では画面幅が固定になっているので、これを自由に変更できるようにします
f:id:ryokwkm:20151215124140p:plain

これで大きさを動かせるようになりました
色のついたViewを置いて、このステップは完了です
f:id:ryokwkm:20151215124143p:plain


2.xibとクラスを紐付ける

まず、紐付け先のクラスを書いていきましょう
先ほど作成した、 ToolBarView.swift に以下のように記述してください

import UIKit

class ToolBarView: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        comminInit()
    }
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        comminInit()
    }
    
    private func comminInit() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "ToolBarView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil).first as! UIView
        addSubview(view)
        
        let bindings = ["view": view]
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options:NSLayoutFormatOptions(rawValue: 0), metrics:nil, views: bindings))
        addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options:NSLayoutFormatOptions(rawValue: 0), metrics:nil, views: bindings))
    }

}


初期化時にcomminInit()を実行していますが、
これは自身に ToolBarView.xib のレイアウトを設定しているものだと思ってください


さて紐付け先もできたので、xibと紐付けていきましょう

Placeholders > File's Owner を選択し、クラスを設定します
ここでクラスを設定するのはViewではありません。
Placeholders > File's Owner にクラスを設定するのはカスタムView特有の作業です、注意してください。
f:id:ryokwkm:20151215124549p:plain



3.storyBoardとカスタムView紐付けて、動作確認

storyBoardとカスタムViewの紐付けを行い、実際に動作確認を行います

といっても簡単です
以下のようにViewをおいて、そのクラスに紐付けたいカスタムViewのクラスを指定するだけでOKです

f:id:ryokwkm:20151215124514p:plain


これで完了。

それでは動作確認してみましょう

f:id:ryokwkm:20151215124516p:plain

ちゃんとカスタムViewでおいたピンク色が表示されていますね


それでは次回は、実際にボタンを配置していきます

ryokwkm2.hatenadiary.jp

【Swift1.2】 単純なファイルの読み込みから、CSVデータによるCoreDataの初期化まで【MagicalRecord】

こちらの記事はSwift1.2版です
Swift2.0のコードはこちらにあります
ryokwkm2.hatenadiary.jp


今回の記事では、3つのことを行います

◆1.テキストファイルを読み込み、一行ずつ出力
◆2.CSVファイルを読み込み、カンマ区切りでデータを取得(出力)
◆3.CSVデータでCoreDataのデータを初期化。(MagicalRecordを使用)

今回やりたかったのは、CoreDataの初期化です
アプリインストール時、DBに初期データを挿入したかったのですが
CSVデータから挿入がスムーズだろうということで、このようなことを調べました。

MagicalRecordとは、CoreDataを手軽扱えるライブラリです
今回はMagicalRecordを使用してDB操作をしています

ステップを踏んで説明していきます



◆1.テキストファイルを読み込み、一行ずつ出力

Memo.csvというテキストファイルを読み込み、それを出力します

let filePath = NSBundle.mainBundle().pathForResource("Memo", ofType: "csv")

let data = NSString(contentsOfFile: filePath!, encoding: NSUTF8StringEncoding, error: nil) as! String
data.enumerateLines{(line, stop) -> () in
  println(line)
}


◆2.CSVファイルを読み込み、カンマ区切りでデータを取得(出力)

Memo.csvというcsvファイルを読み込み、それを出力します
(配列への格納までです)
ちなみに中身はこんなデータです

memo1, メモ1です
memo2, メモ2です
memo3,
memo4,
let filePath = NSBundle.mainBundle().pathForResource("Memo", ofType: "csv")

let data = NSString(contentsOfFile: filePath!, encoding: NSUTF8StringEncoding, error: nil) as! String
data.enumerateLines{(line, stop) -> () in
  //カンマ区切りで出力(配列に格納まで)
  let item:[String] = split(line) {
    $0 == ","
  }
  println( item )
}


◆3.CSVデータでCoreDataのデータを初期化。(MagicalRecordを使用)

Memoテーブル
//構造は title:String, value:String
CSVデータで初期化します

はじめにテーブルの中身を見て、空の場合にデータを挿入しています


//Memoテーブルに保存。
//構造は title:String,  value:String  
var memos:[Memo]! = Memo.MR_findAll() as! [Memo]

if let first = memos?.first {
	//データが存在した場合は初期化しない
} else {
	//データ取得できなかった場合、初期データ挿入
	let filePath = NSBundle.mainBundle().pathForResource("Memo", ofType: "csv")
	
	let data = NSString(contentsOfFile: filePath!, encoding: NSUTF8StringEncoding, error: nil) as! String
	data.enumerateLines{(line, stop) -> () in
		let item:[String] = split(line) {
			$0 == ","
		}
		
		var newRecord:Memo = Memo.MR_createEntity() as! Memo
		newRecord.title = item[0]
		if item.count > 1  {
			newRecord.value = item[1]
		} else {
			newRecord.value = ""
		}
		newRecord.managedObjectContext!.MR_saveToPersistentStoreAndWait()
	}
}	
	
}


まとめたもの
ファイルを読み込む · GitHub