2014年07月28日

金融機関のMITB対策について

前からMITB攻撃という言葉がよく金融機関相手に出てきています。
それでも十分知れ渡ってきているため、最近では金融機関以外のサービスも狙われてきています。

MITB攻撃=Man In The Browser攻撃の略です。
簡単に言えば、対象者のPCにトロイの木馬を仕掛け、被害者が銀行のサービスを利用しているときに、被害者が気づかない間に不正にブラウザを操作してしまう攻撃です。
この攻撃を受けてしまうと、被害者は気づかない間に、金融機関のサービスであれば加害者の口座に不正に送金する作業を勝手に行われてしまいます。

この攻撃に対しての対策は大まかに三つあります。
1. 二要素認証
2. 専用のセキュリティ対策ソフトウェアの導入
3. トランザクション署名

1. 二要素認証は簡単です。ログイン時にパソコン以外の独立した別のデバイスでもログインをすることを意味します。たとえば携帯電話などを利用します。これによって勝手にログインされてしまうリスクを減らすことができます。ですが、ログインできてしまえば攻撃できるのですから、攻撃者は対象者がログインするまで待つことで、攻撃を可能にできてしまう可能性も指摘されています。

2. 専用のセキュリティ対策ソフトウェアの導入は、これまでのウィルス対策ソフトウェアよりももっと高度で、MITB攻撃に特化した検出に強みを持つソフトウェアです。
接続しているサイトが偽物ではないか検出すること、不正なソフトウェアがないかを検出すること等、各種対応が実装されていますが、まずこのソフトウェアを導入する前に、不正なソフトウェアが導入されていた場合には効果がない可能性があります。また、こういったソフトウェアは銀行からは無償でユーザーに提供されます。よって、攻撃者もこのソフトウェアを解析できてしまい、バグを突かれる可能性も否定できません。

3. トランザクション署名は、ワンタイムトークンを発行するハードウェアなどを併用し、送金をブラウザから実施した場合、そのハードウェアに表示された番号も同時に入力する必要があるものです。
そのハードウェアが発行する番号は、一定の時間で変化し、変化の仕方は端末ごとによって異なります。よって、攻撃者は利用者が持っているハードウェアをリアルタイムで見られる環境になければ、勝手にブラウザを乗っ取っても、同時に必要な番号がわからず、処理が受理されません。
この方法は非常に有効ですが、まずハードウェアが必要であること、そして、そのハードウェアの情報をすぐに盗み見ることができる状況、つまり、たまたまWebカムがPCについていて、Webカム経由でのぞかれていたなど、レアケースには対抗しにくい点が課題かもしれません。

よって、通常は上記3種類の組み合わせが必要となります。

でも、もっと単純な方法があると思います。
被害が発生するケースは、不正に送金されるなどの特定のオペレーションです。そのオペレーションがされた時だけ、携帯などの二要素認証に利用する端末にメールを送信し、そのメールにどういうオペレーションを現在しようとしているかサマリが表示されるようにします。
その内容が正しければ、メールにある認証情報をもとにオペレーションを継続するよう指示を出せば処理が完結するようにします。

利用者は不正な操作があればメールで気づき、その後の作業は承認しなければよく、同時に自分の操作もメールで記録を残せます。

もしかしたら私が知らないだけで、ログイン以外にも上記のような二要素認証が適用されているのかもしれませんが、具体的な内容にあえて触れず簡単にしか説明しませんでしたが、不正被害にあいやすいオペレーションの結果も二要素認証の対象としてしまえば、もう少し簡単かつ確実な対策があるのではないかと感じますが、いかがでしょうか。
posted by Kiruahさん at 01:00| Comment(0) | TrackBack(0) | ノウハウ

2012年07月17日

Struts1のhtml:checkboxではまりました。。。2

ちなみにですが、Struts1のActionFormにはresetメソッドをオーバーライドすることで、一応はチェックボックスを一度初期化することができます。

よって、ブラウザからリクエストを受けたタイミングで一度チェックボックスの変数のみfalseにし、もしブラウザから値が送信されていればtrueに変更することで、チェックボックスの状態を扱うことはできます。
実装するとすると以下のようになろうかと思います。

・ActionForm

package com.sample;

public class Form extends ActionForm {

/** チェックボックス */ // ここはhtml:checkboxのproperty属性名と一致
private boolean check1 = false;

// setter/getterは省略

public void reset(ActionMapping mapping, HttpServletRequest request) {

check1 = false;
}
}


ですが、画面の仕様やフォームの仕様というものは変更されることがあります。
ActionFormは普通には画面の入出力情報を保持している入れ物と考えておくほうが非常に分かりやすく、その中に例外的にresetメソッドが実装されている状態ですと、仕様変更時にresetメソッドの実装修正が発生してしまい、バグに繋がる恐れがあります。

一応、もし実装と設計とそれぞれの相関を完全にデータとして保持し、影響調査により修正すべき箇所を洗い出すことができるならば、resetメソッドは有効に働くでしょう。

ですが、私の個人的な意見としてはresetメソッドやActionFormにはvalidateメソッドを実装できますが、それらはActionFormでオーバーライドしないほうが良いのではないかと考えています。
できる限りそうでない方法を検討し、各クラスや成果物の責務を明確かつシンプルにしておくことを心がけるべきだと考えています。

ちなみにですが、StrutsのJavadocでも理由はいくつかあげられていますが(チェックボックスを初期化するのが必要なら仕方なしとして)、オーバーライドしないことを推奨しています。特にセッションスコープに格納しつつ、複数画面で使いまわす場合には要注意です。

一応、忘れていないですよっていう意味も込めての補足で記載しました。
posted by Kiruahさん at 22:41| Comment(0) | TrackBack(0) | ノウハウ

2012年07月14日

Struts1のhtml:checkboxではまりました。。。

久しぶりにStruts1を見ました。。。理由はまぁ色々とありまして。。。

元々StrutsでCheckboxをどういうふうに処理すべきかなんていうのは覚えておりません。
ただ、ブラウザはWebサーバーにチェックされていないCheckboxについては値を送信しないことは知っていましたが、実際jcwebを使うとJavaの中から直接Checkboxの状態を取得できてしまいますし、仕事ではCheckboxはあまりつかわないようにしていたので考えたことがありませんでした。(これはダメですね。。。しかもJSFの他のOSSライブラリを使っていたり)。

で、聞いた話によると、どうもうまくStruts1でCheckboxは未だ難しいようです。
具体的には以下の場合です。
・2画面の画面遷移であり、共に同じActionFormを使う
・1画面目でとある条件(DB検索した結果を入れるだけですが)を満たすと、チェックを画面につけた状態にする
・そうでなければチェックしていない状態にする
・チェックがついている状態で表示された場合、チェックを外してサブミットしても、チェック状態と同じ状態でブラウザから値が送信されてくる

環境としては以下です。
・Windows 7 (無関係でしょうが)
・IE9
・JDK6 (無関係でしょうが)
・Tomcat 7.0.29 (無関係でしょうが)
・Struts 1.3.10
・画面はJSP (無関係でしょうが)


実際に私もデバッグしてみると、valueの値が送られるとかそういう感じでもなく、checkedの属性があればon、なければoffが送られるような感じの挙動でした(忙しかったのでcheckedの値とvalueの値がどうこうまでの検証まではしていません。ごめんなさい。)。
なので、特効薬としてこんな感じにしました。

・struts-config.xml







・ActionForm

package com.sample;

public class Form extends ActionForm {

/** チェックボックス */ // ここはhtml:checkboxのproperty属性名と一致
private boolean check1 = false;

// setter/getterは省略
}

ポイントとしては、型は必ずbooleanを使うところです。

・JSPの中身(抜粋)


チェック
送信



JSPについては、実は以下の実装の方がいいのかもしれません。ifで書いてますが、論理否定に変えた方が美しいでしょう。

・JSPの中身(抜粋)


チェック
送信


あとは、onclickの箇所を別functionにするとか、どのブラウザでも確実に動作するためにjQuery化するとか、色々と工夫の仕方はあるでしょう。

ちなみにですが、SyntaxHighlighterを使っておりますが、勝手にタグ部分が大文字になっております。ソース上は全部小文字で記述しておりますのでご注意ください。


なんとなくやってみて、なんとなく動作したものなので、もっといいやり方があればご教授ください。
posted by Kiruahさん at 19:23| Comment(0) | TrackBack(0) | ノウハウ

2012年07月01日

JSF実装のELライブラリをスタンドアロン・プログラムの中で利用する

Javaによる画面アプリケーションでは、EL式と呼ばれるものがJSPやJSFに組み込まれて利用されます。
そのEL式は、やっぱり基本的にはWebアプリケーションサーバー上で利用することが前提でよく記述されています。一応EL式のライブラリ自体は切りだされていますが、いまいち使い方が分からないです。

例えばスタンドアロンアプリケーションを作り、その中で便利だからELだけ使いたいという場合。
そういう場合でもELのためだけにWebアプリケーションサーバーを導入したり、もしくはそのためにJSFライブラリ全体を持ってきたりするのは面倒です。
ApacheのJEXLApache Commons EL等のライブラリでも良いのですが、ELはGlassFish(mojarra)の実装がやはり一番速度が速いようなので、こちらを何とかして使いたいというかたも多いでしょう。でも、簡単に何もせず、APIだけで組み込むことができなくて苦労します。また調べても、すぐには何も見つかりませんでした(英語でも調べたんですが。。。)

なので、どうやったら簡単に自分のアプリケーションにEL式を組み込めないかなと考えていました。
(考えていただけで、家でのプログラムではEL式を利用するつもりは今のところないのですが)
色々と試してみましたが、スタンドアプリケーション用のELContextとELResolverを実装するしかないのかなぁという結論に至りました。
もっといい方法があるかもしれません。。。

参考までにソースを紹介しますので、ご自由にご利用くださいませ。Javadocコメントは若干適当です。

LocalELContext.java (ELContextと中で利用するELResolver実装です。)

import java.beans.FeatureDescriptor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.el.ArrayELResolver;
import javax.el.BeanELResolver;
import javax.el.CompositeELResolver;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.ListELResolver;
import javax.el.MapELResolver;
import javax.el.VariableMapper;

import com.sun.el.lang.FunctionMapperImpl;
import com.sun.el.lang.VariableMapperImpl;
import com.sun.faces.el.PropertyResolverChainWrapper;
import com.sun.faces.el.PropertyResolverImpl;

/**
* ローカル変数を起点としてELを実行するためのELコンテキスト
*
* @author Kiruah
*/
public class LocalELContext extends ELContext {

/** ELContext初期化済みかどうかを指定する */
protected static Boolean initialized = Boolean.FALSE;

/** ローカルEL解決者 */
protected static LocalELResolver resolver = null;

/** メソッド呼び出しマッパー */
protected static FunctionMapper functionMapper = null;

/** 変数取得マッパー */
protected static VariableMapper variableMapper = null;

/**
* 初期化処理
*
* @param object 取得対象のオブジェクト
*/
public LocalELContext(
Object object) {

if (initialized == false) {
initialize();
}

if (resolver == null) {
resolver = new LocalELResolver(object);
}
}

/**
* 初期化処理
*/
protected void initialize() {

synchronized (initialized) {

if (initialized == false) {
if (functionMapper == null) {
functionMapper = new FunctionMapperImpl();
}

if (variableMapper == null) {
variableMapper = new VariableMapperImpl();
}

if (LocalELResolver.compositeResolver == null) {
CompositeELResolver elResolver = new CompositeELResolver();

elResolver.add(new ListELResolver());
elResolver.add(new MapELResolver());
elResolver.add(new ArrayELResolver());
elResolver.add(new BeanELResolver());
elResolver.add(new PropertyResolverChainWrapper(new PropertyResolverImpl()));

LocalELResolver.compositeResolver = elResolver;
}

initialized = Boolean.TRUE;
}
}
}

/**
* EL解決者を取得する
*

*

(オーバーライドメソッド)


* @return EL解決者
* @see javax.el.ELContext#getELResolver()
*/
public ELResolver getELResolver() {

return resolver;
}

/**
* メソッド呼び出しマッパーを取得する
*

*

(オーバーライドメソッド)


* @return メソッド呼び出しマッパー
* @see javax.el.ELContext#getFunctionMapper()
*/
public FunctionMapper getFunctionMapper() {

return functionMapper;
}

/**
* 変数取得マッパーを取得する。
*

*

(オーバーライドメソッド)


* @return 変数取得マッパー
* @see javax.el.ELContext#getVariableMapper()
*/
public VariableMapper getVariableMapper() {

return variableMapper;
}

/**
* ローカルEL解決者
*
* @author Kiruah
*/
protected static class LocalELResolver extends ELResolver {

/** ネストされたEL解決者 */
protected static CompositeELResolver compositeResolver = null;

/** 探索対象のオブジェクト */
protected Object object = null;

/**
* コンストラクタ
*
* @param object EL実行対象のオブジェクト
*/
public LocalELResolver(
Object object) {

this.object = object;
}

/**
* 値を取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @return 取得結果
* @see javax.el.ELResolver#getValue(javax.el.ELContext, java.lang.Object, java.lang.Object)
*/
@Override
public Object getValue(
ELContext context,
Object base,
Object property) {

if (base == null) {
return compositeResolver.getValue(
context,
object,
property);
} else {
return compositeResolver.getValue(
context,
base,
property);
}
}

/**
* 型を取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @return 型
* @see javax.el.ELResolver#getType(javax.el.ELContext, java.lang.Object, java.lang.Object)
*/
@Override
public Class getType(
ELContext context,
Object base,
Object property) {

if (base == null) {
return compositeResolver.getType(
context,
object,
property);
} else {
return compositeResolver.getType(
context,
base,
property);
}
}

/**
* 値を設定する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @param value 設定値
* @see javax.el.ELResolver#setValue(javax.el.ELContext, java.lang.Object, java.lang.Object, java.lang.Object)
*/
@Override
public void setValue(
ELContext context,
Object base,
Object property,
Object value) {

if (base == null) {
compositeResolver.setValue(
context,
object,
property,
value);
} else {
compositeResolver.setValue(
context,
base,
property,
value);
}
}

/**
* 読み取り専用か取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @param property EL式
* @return 読み取り専用かどうか
* @see javax.el.ELResolver#isReadOnly(javax.el.ELContext, java.lang.Object, java.lang.Object)
*/
@Override
public boolean isReadOnly(
ELContext context,
Object base,
Object property) {

if (base == null) {
return compositeResolver.isReadOnly(
context,
object,
property);
} else {
return compositeResolver.isReadOnly(
context,
base,
property);
}
}

/**
* 特徴の説明情報を取得する
*

*

(オーバーライドメソッド)


* @param context ELコンテキスト
* @param base 探索対象
* @return 特徴の説明情報
* @see javax.el.ELResolver#getFeatureDescriptors(javax.el.ELContext, java.lang.Object)
*/
@Override
public Iterator getFeatureDescriptors(
ELContext context,
Object base) {

if (base == null) {
return compositeResolver.getFeatureDescriptors(
context,
object);
} else {
return compositeResolver.getFeatureDescriptors(
context,
base);
}
}

/**
* 共通のプロパティタイプを取得する
*

*

(オーバーライドメソッド)


*
* @param context ELコンテキスト
* @param base 探索対象
* @return 共通のプロパティタイプ
* @see javax.el.ELResolver#getCommonPropertyType(javax.el.ELContext, java.lang.Object)
*/
@Override
public Class getCommonPropertyType(
ELContext context,
Object base) {

if (base == null) {
return compositeResolver.getCommonPropertyType(
context,
object);
} else {
return compositeResolver.getCommonPropertyType(
context,
base);
}
}
}
}


この中では、ELライブラリで標準で実装されている
・ListELResolver
・MapELResolver
・ArrayELResolver
・BeanELResolver
・PropertResolver
のみCompositeELResolverにセットし包含しています。
ELはトップノードのアクセス時のみgetValueのbaseがnullとなりますので、その場合のみ指定のオブジェクトを探索するようにし、それ以外はbaseを利用するようにするという程度のものです。
初期化処理は冗長です。static {} を使えばいいのですが嫌いなのでこういう感じになっています。

利用する場合は以下の様な実装となります。

Main.java

import java.util.HashMap;
import java.util.Map;

import javax.el.ELContext;
import javax.el.ExpressionFactory;

import com.sun.el.ExpressionFactoryImpl;
import com.sun.el.ValueExpressionImpl;

/**
* EL組み込みのサンプル
*
* @author Kiruah
*/
public class Main {

/**
* メイン
*
* @param args コマンドライン引数
*/
public static void main(
String[] args) {

Map container = new HashMap();
container.put(
"hoge1",
"hogehoge1");
container.put(
"hoge2",
"hogehoge2");

String el = "${hoge1}";
Object value = get(
container,
el);

System.out.println(value);
}

/**
* EL式を利用して取得対象から値を取得します。
*
* @param target 取得対象
* @param el EL式
* @return 取得結果
*/
public static Object get(
Object target,
String el) {

ELContext context = new LocalELContext(target);
ExpressionFactory ef = ExpressionFactoryImpl.newInstance();
ValueExpression ve = (ValueExpression) ef.createValueExpression(
context,
el,
Object.class);

Object value = ve.getValue(context);

return value;
}
}


こうすることで、自前で用意しているコンテナがあれば、そこからEL式を利用して値を取得することができます。

ちょっと実装しなければならないとできなかったのが、自分の調査・スキル不足ならば、もっと勉強や自己研鑚が必要です。
頑張らねば。
posted by Kiruahさん at 01:00| Comment(0) | TrackBack(0) | ノウハウ

2012年06月16日

JSP/JSFでの項目をフォーマッティングするタグをできる限りやめてみる

JSPやJSFの魅力の一つにカスタムタグがあります。タイトルはこのカスタムタグを否定しています。
カスタムタグを利用することで、モデル層・コントロール層でいちいち画面に表示する内容を整形する必要はありません。これらは全てどのように表示するかをビュー層に任せることができ、MVC構造上分かりやすい(責任境界が明確)と言えます。

例えば仕様変更によって、画面上の日付をyyyyMMddからyy/MM/ddに変更する必要が発生した場合、カスタムタグ(これぐらいならJSPの標準タグでできそうですが)を用いれば、ビュー層まではDateやTimestamp型で送られてきているため修正は不要です。結果、JSPのその出力タグのどのように表示するかを指定する属性を変更するのみで、修正は終了です。

ポイントは、修正はこれで終了である、という点です。

テストはというと、画面を実際に表示する必要があります。
さて、ここでこのカスタムタグが十分にテストされていることを前提としている場合でも、本当に問題がないのかを確認するには、JSPを表示してみる必要があります。これで実はMMの部分が月ではなく、分を指定していたとしても、たまたま12分までの間にテストをしていたら、修正ミスに気付けません。よって、テストでは12分以上をしっかりと確認する必要があります。
現状の開発では、Seleniumを使わず結局目視で確認しています。また、目視でもしっかりとしたテストケースをあげているケースもレアです。こういう場合は、しっかりと最小桁、最大桁のテストとして、00と12はテストする必要があります。仕様をしっかり確認し、分のところの00は0になるのか00のままなのか、分になっていないのかを確認する必要があります。

さて、seleniumを使っていたとしても、仕様変更のためにシナリオを変更してここまで確認するのは面倒です。seleniumはやっぱり目視での確認後に、デグレが発生していないかを確認するためのものとして現場では利用されているのでしょうか?

弊社では、seleniumすら利用しようという空気はないため、目視1ケースのみで終了となり、同時に回帰テストは二度と行われません。
よくあるのが、ここで結局ミスをしていたというのも多発しています。


カスタムタグが十分にテストされているケースであればまだ良しと言えます。このカスタムタグが弊社自作の場合で、かつ、十分なテストが実施されていない場合は、このカスタムタグをテストしているのか分からない状況になることもあります。


最近のWebアプリケーションの見栄えはとてもよく、例えば長い文字列を表示する場合は、"..."と省略形に自動的になり、マウスを近づけるとチップ形式で全文が表示されます。
業務アプリケーションにおいては、技術が時代に取り残されているのもあります(枯れたものを好んでいると理解しておきましょう)が、業務では一覧性が重要視されるために、文字列が省略されることなど滅多にありません。
つまり、表示したい内容はまさに、そのまま書式化されたものを単純に表示すれば事足ります。


カスタムタグにあるような、書式を自由に変換できるタグは一切利用せず、これらは全てビュー層の前、ビジネスロジックで実施すべきです。そして、ビュー層ではその値がそのまま表示されるかどうかだけを確認します。

こうすることによって、ビュー層とビジネスロジック層のテストを分離しやすくしたいと考えています。

・ビュー層
ビュー層では、ビジネスロジック層のモックがフル桁および、最小桁のデータを渡すようにしておきます。
これがレイアウト上崩れないことを確認するのみのテストとすることで完結させます。
よって、ビュー層のみの単体テストを実施します。

・ビジネスロジック層
こちらはビュー層に渡すデータを出力として、JUnitでアサートするテストとなります。
つまり、日付であれば、テストケースとして正しくビュー層に表示すべき内容を変換してから渡しているか網羅的にテストすることとなります。こうすることによって、単純な書式の変更や変換の不具合の確認は、ビュー層で確認するよりもはるかに回帰テストしやすいものにできます。
ビジネスロジック層はそれ単体のみの単体テストを実施します。

・ビュー層とビジネスロジック層の橋渡しを行うBean
これはフレームワークにもよりますが、正しくビュー層・ビジネスロジック層で利用されているか、確認しやすいよう、それぞれ分かりやすい命名規約を用いることを推奨します。

・結合テスト
こちらはレイアウトや表示内容に関する網羅的な結合テストは実施しません。あくまで機能に特化したテストを実施し、ビューとビジネスロジックで正しい業務処理が行われるかの確認を行います。
ページ遷移はやむをえないでしょうが、フル桁はどうか、フォーマットはどうかなどの確認は不要です。そのままビジネスロジック層のデータが出ているかを確認します。
(当然、各項目ごとに値を変えて、出すべき変数名を間違えていないかを確認するのは言うまでもありません。こればかりはJSPやJSFでは確認しようがないでしょう。)


まとめると、私は、ビジネスロジック層で画面に表示する全項目の書式変換を行うべきだと考えています。


これはメリット・デメリット両方あります。
メリットは、Seleniumのシナリオで網羅性はあまり考えなくて良いでしょうし、JUnit/TestNGを利用するのでJenkins/Hudsonでバックグラウンドでテストさせておくと良いでしょう。また、カスタムタグだけでは表現しきれないものは、工数見合いで結局ビジネスロジック層に実装することが多いため、ビジネスロジックに集約することでライブラリ化が容易になります。

デメリットは、JSPの修正だけであれば、JSPを置き直すだけでリコンパイルされるのですが、ビジネスロジックを修正する場合はアプリケーションサーバーのホットデプロイか、サーバーの再起動が必要なのが面倒でしょう。
もとから変更はサーバーを止めてしっかり検証するでしょうが、緊急対応には向かないかもしれません。
posted by Kiruahさん at 18:09| Comment(0) | TrackBack(0) | ノウハウ

2012年06月14日

これから始めるSubversionの使い方

弊社では、未だにSubversion(svn)を利用してソースやドキュメントのバージョン管理をしています。
まだ、ドキュメントをバージョン管理している案件はましなのですが、未だに全部を共有ファイルサーバー上で管理して、編集前にバックアップフォルダに日付を付けて移動。。。ということをやっている方々も多いです。

Subversionですら、浸透するまでに4年近くかかりました。本当はMercurialかGitにしたいのですが、TortoiseSVNから離れられない人が多いのと、新しいことを覚えたくない人と、分散バージョン管理を理解できない人のいずれかが多く、Subversionでうまいこと競合等を管理する必要があると考えていました。

「うまいことやる」ということは、よろしくない状況が発生しているということです。
具体的には以下の様なことが多発しています。

・複数人で開発していると誰かがソースを上書きしてコミットしてしまう(確認不足)
・エラーが発生したままでソースをコミットし、全員がエラー発生状態になる
・そして、エラーが発生していても本人はなかなか修正しない。。。
・動かない設定や自分ローカルで勝手に変更した設定をコミットする
・コミットとバックアップを勘違いしている
・複数人がtrunkで開発するので、どうしても仕方なく構成を変更したい場合、全員一時的にコミットさせ、構成変更後に全員チェックアウト/更新しなおし
あとは個人的な話ですが。
・svn:needs-lockがかかっていると、ちょっと面倒臭い。。。
・svn:needs-lockをかけていると、たまに削除も何もできないファイルがリポジトリサーバに取り残させる。。。
・結局ロックは奪えてしまうので、誰でも変更できる

リポジトリの管理やユーザー管理については、Windows限定ですが、VisualSVNという素晴らしいソフトウェアが無料で提供されており、大変重宝しています。


すでに構築されてしまっているsvnは仕方なしとして、これから新規に始める案件で、svnも新規に作れる案件ではこうしようと考えています。


1. ディレクトリ構成
従来までは以下の様な構成でした。
-+branches
+tags
+trunk

これをこのように変更します。

-+branches
+tags
+releases
+trunk
+groups
+users

・branches
個別の仕様変更ごとにディレクトリ名をつけてtrunkの内容をコピーします。
フォルダ名は案件にもよりますが、通常は以下のいずれかようになると考えています。
1) 日付-内容
2) 案件管理番号-内容
3) 案件管理番号-日付-内容
4) 顧客名/日付-内容
5) 顧客名/案件管理番号-内容
6) 顧客名/案件管理番号-日付-内容
(/はディレクトリをさらに作ることを意味しています。)

・tags
プロジェクト内マイルストーン単位でディレクトリ名をつけてtrunkの内容をコピーします。
フォルダ名は案件にもよりますが、こちらも通常は以下のいずれかようになると考えています。
1) 日付-内容(理由)
2) マイルストーン名-内容(理由)
3) マイルストーン名-日付-内容(理由)

・releases
通常は顧客には複数の環境が用意されています。例えば、顧客検証用、顧客受入用、顧客内ベンダー開発機、リリース検証機、本番機、災対機(待機系)。さらには自社の開発環境、テスト環境(単体/結合/総合等)があります。
それぞれ、どのパッチをどこまで適用するか異なる場合があります。それが順次段階を経て適用されるのであれば良いのですが、これまで数回、緊急のためにすぐにリリース検証機で簡単な検証後、本番に適用し、数日後に他のサーバーに適用ということがありました。
そして、運が悪いことに顧客都合によってそのスケジュールが延びることもあります。これらをどこまで適用したか管理するためにExcel管理台帳とか、共有フォルダにおいていたりしましたが、これをsvnのreleasesにディレクトリ単位で管理したいと考えています。

同時に、パッケージやそのカスタマイズ結果など、複数の顧客を管理する必要がある場合にも利用します。

具体的には以下のディレクトリ構成を採用します。

releases/顧客名/No-環境名
Noは適用順序を示します。二桁で統一します。(1なら01とする)

・trunk
trunkはこれまで通りの利用方法に近いです。安定版の最新を入れる形になります。
が、こちらもディレクトリ構成を分ける方が良いでしょう。

1) trunk/stable
2) trunk/develop
3) trunk/チーム名/
4) trunk/顧客名/

などです。

・groups
こちらは用意するかどうかは案件の規模によります。
大きい場合はグループごとにディレクトリを分けます。

groups/グループ名/

このグループの下は、グループ単位で成果物を取りまとめる場合に利用します。
つまり、グループ管理者が一度ここにユーザーの成果物をとりまとめ、問題がなければtrunkにマージします。

・users
こちらは開発者単位でディレクトリを作成します。

users/ユーザー名/
ユーザーが自由にコミットしてよいディレクトリとなります。

groupがある場合はユーザーはgroupにのみマージできます。また、groupがない場合はtrunkのdevelopにのみ、ユーザーはソースをマージできます。


2. ユーザー
ユーザーとグループの管理をきっちりと行います。
一般ユーザーはusers/配下の自分のディレクトリにしかコミットできません。よって、チェックアウトもそこから行います。案件に参画したタイミングで、trunkの必要な情報を自分でコピーします。
また、場合によってはgroupsに自分のコミット分をマージできます。groupsがある場合は一度groupsにコミットし、trunkの反映はそのグループの管理者のみが行えます。逆にgroupsがない場合はユーザーはtrunk/developにマージできますが、trunk/stableへのマージは案件のライブラリ管理者がチェック後、行います。


3. ライブラリアンをたてる
ライブラリアンしか、ユーザーの管理、グループの管理が行えません。通常最大でも3名とします。
さらにグループ毎に1名をグループのサブライブラリアンとし、trunk/developにコミットする権限を有します。
ですが、trunk/developからtrunk/stableやbranches、tagsにはライブラリアンしか変更できません。


4. 個別要件(ブランチやタグ)の管理
ライブラリアンは案件上ブランチやタグに直接コミットできるユーザーを指定し、個別機能の修正を許可することができます。


5. needs-lockは使わない
ロック機能は使わないこととします。また、ライブラリアンも直接stableを変更せず、自分のユーザーディレクトリの中で変更後、trunk側にマージすることとします。


6. ライブラリアンはtrunk/stableを変更した場合、ユーザーは任意のタイミングでtrunk/stableをマージできる


7. できる限り、グループの管理者、ライブラリアンが修正結果の連絡を受け取りマージする。つまり、ユーザーには本体のtrunk等には変更権限を与えない


まだまだやり方や構成については検討の余地があると思います。それは管理がまだ面倒そうだというところです。
ですが、これでかなり管理者の自由度が上がるのではないかと考えています。
もう少しドキュメント化したり、実際にやってみてブラッシュアップしたいと考えています。
posted by Kiruahさん at 20:00| Comment(0) | TrackBack(0) | ノウハウ

2012年04月09日

InternetExplorerの古いバージョン(6とか7)から9へそのままHTMLを移行した場合の差異

分かっていた以上に、InternetExplorerのバージョンの違いによって挙動が変わってきており、少々びっくりしました。
まず6と7ではタブが追加されましたので、単純に同一ウィンドウのタブではセッションが共有されてしまうことがあるのもびっくりした記憶があります。
理由はタブが違うだけではクッキーは共有され、クエリ文字列ではなくセッションキーをクッキーに持たせたためだからなのですが。。。
私がまだまだ絶賛開発中のjcwebの中ではセッション情報はページ単位にJavascriptに持たせるようにしようとしています。
そして、毎回セッションキーは使い捨てです。つまり、毎回サーバー側でセッションキーをアクセスさせるたびにワンタイムパスワードのように発行します。
初回はサーバから送られてきたセッションキーをクライアントはそれをAjaxのPOSTパラメータに詰めないといけないという仕様です。
そして帰ってきたレスポンスに新しいセッションキーがあり、次はそれをPOSTパラメータに詰めるということを繰り返します。

この話はまぁ、置いておいて。
ここではよくあるような違いではなく、あまり気づきにくい所を紹介したいと思います。

スタイルシートを利用していない箇所にて、以下のような状況が発生しています。

1. font-sizeを指定しており、互換モード以外の場合、フォントサイズがIE9では大きくなっています。
font-size: small; と指定していた場合、これはIE9では、IE6のfont-size: medium; と同程度の大きさで表示されます。
よって、文字サイズが大きく表示されます。smallやmedium、bigなどで指定している場合は順次大きさがずれています。

2. 行間が小さくなっています。
これはフォントサイズが大きくなっているため、あまり感じにくい面もありますが、特に指定していない場合

3. legendタグのデフォルト色が水色から黒色になっています。

4. 文字の大きさの変更の影響を受けてか、テキストボックスの大きさ、余白も少し変わっています。
これは気づきにくいですが、比べてみると全体的に前よりもアンバランスな見た目に見えます。

また、別途気付いた点があれば随時追加していきたいと思います。
posted by Kiruahさん at 21:00| Comment(0) | TrackBack(0) | ノウハウ

2012年04月02日

現行からのリプレース案件、マイグレ案件の注意点について

こちらは以前にも似たことを書いたことがあります。

システムの再構築について http://kiruah.sblo.jp/article/45437976.html

今関わっている案件もリプレース案件です。この案件は基本現行をそのままプログラムを変更せず
新しいハードウェアに移行するというものです。
ですが、その中に一部仕様変更を盛り込みたいというものがあります。
私は何度でも言いますが、まずは移行だけをしましょう。
その後に仕様変更を取り込むようにすべきです。

でなければ、

1. バグが現行からなのか、それとも新規に追加した仕様なのかの調査が複雑になります。
2. 工数の計算が複雑になります。なぜなら移行なのか、それとも仕様調査なのか、修正なのかどちらのテストを実施しているのかが曖昧になります。これは工数請負の場合、特にユーザ側に後々不利になります。曖昧さは、こちらのマイナスを別の理由に転嫁できてしまうためです。
3. 若手がいる場合、教育工数が非常にかかります。当然現行の知識を知り、その上で修正分を理解する必要があります。まずは現行を移行しテストさせ理解した後で、変更分を理解させるほうが教育工数が減ります。
4. 経験的にですが、スケジュールの立て方が往々にして曖昧になりやすく、これぐらいで両方できるだろうという程度のスケジュールを構築しがちです。よって、後ほど時間が足りなくなることが多いです。
5. 後戻りが簡単です。移行が完了していれば、最悪仕様変更を取り込んでいないモジュールを使い、現行レベルを維持してリプレース運用は可能です。それが同時に行われた場合、切り離しが必要か、一部機能は問題がないとしても全機能の実装完了が必要となってしまい、ユーザのビジネス機会が損なわれやすくなります。

幸いにして、今の案件は現行調査と実際の修正はフェーズを分けることを全員が認識していたため、まずは現行のリプレースのみが完了しました。
よって、その後に仕様修正分を集約し変更するだけですので、横目に見ても作業は簡単に見えます。

SIerは一気に詰め込んでしまえ!と、何でもかんでも混ぜ込みすぎます。一つ一つを順次やるほうが、後戻りも楽ですし、差分検出、過去からの不具合なのかを明確に順次トレースできます。
やり方、考え方はいつも、「急がば回れ」です。
posted by Kiruahさん at 22:00| Comment(0) | TrackBack(0) | ノウハウ

2012年03月28日

Excel2003からExcel2010に移行するにあたって

Excel2003で構築したマクロをExcel2010に移行する場合、いくつか注意点がありましたので連携したいと思います。
あくまで色々と実験してみた結果の個人的な意見です。賛否両論あろうかと思いますので、適宜自分に都合のよいところを取り入れていただければ幸いです。
実験結果は都合により、載せられませんが。。。


1. Excel2003で行を右クリックした時の「形式を選択して貼付け」機能のコントロールIDは変更になりました。よって、このIDを使用しているコードはOffice2007以下かどうか判定し分岐する必要があります。
残念ながら、具体的なコードは都合により載せられないです。。。ごめんなさい。
755で全文検索して見つかる場合は要注意です。
他にも変更になったIDがありそうですが、簡単に調べた限りでは見つけられませんでした。

2. Excel2007から最大行数が増えました。よって、Excel2003で65536のようにハードコーディングしている場合(例えばデータ件数を調べる処理など)は、不具合がでやすいです。
必ずRows.Countを利用し、Excel2003でもExcel2010でも動作するように修正しましょう。
特に65537とかは気づきにくいので、これもRows.Count + 1などと変換しましょう。
推奨は6553で全文検索してみることです。

3. Excel2010では時々、VBAの実行時間が長すぎると、MsgBoxが表示されなくなります。
その場合はMsgBoxのオプションにあるシステムモーダル化(もしくはアプリモーダル化)オプションを指定します。(vbSystemModal もしくは vbApplicationModal)
vbSystemModalはExcel2003にもあるため、コンパイルエラーは発生しません。

4. xlsmに移行するか、xlsの互換モードでいるか悩むところです。が、xlsの互換モードのままを推奨します。なぜならば、xlsmに変換した場合、影響が変換したからか、Office2010のマクロの問題なのかの切り分けが困難であること。もしかしたら、Office2003で再現するか再度確認が必要になるかもしれないことがあること。そして、xlsxやxlsmはある程度の容量を超えると警告なしにデータが保存されないことがある点を考えると、互換モードのままをユーザに推奨するよう説得するのが得策です。

5. よくExcel2010のVBAは遅いと聞きます。大量データを用意し、自前のテキストロード処理を実装している場合、Excel2010のほうが厳密に測った結果速くなりました。
これの原因は互換モードとかではなく、単純に事前にVBAのコンパイルをしていないせいだと思います。コンパイルすれば共に最低でもほぼ同じ速度で動作する実験結果を得ました。

6. Excel2000から脈々と続く、Formで入れたコマンドボタンのサイズやフォント、位置が変わるバグは、Excel2003、Excel2007、Excel2010でも発動します。ただし発動条件はそれぞれのバージョンで違うようです。絶対に全ボタンを押下したり、スクロールさせてみてください。特にスクロール範囲をVBAで変更している場合は要注意です。その場合、Excel2010ではボタンサイズが勝手に変わりました。変わってしまうExcelファイルはどうあがいても変わってしまいます。その場合は問題ないファイルをベースに作りなおしたほうが早いです。試行錯誤したり、ボタンだけを再配置しても無駄のようです。

7. マクロの有効化は、Excel2003は毎回有効にするか聞いてきますが、Excel2010では一度確認するとリセットするまで有効にしたことを覚えてくれます。これは逆にユーザが操作性に疑問を持つので教育が必要です。

8. 印刷プレビューの描画範囲がExcel2010では小さくなるため、若干見にくくなることをユーザに理解させる必要があります。

9. 描画のずれは、Excel2003でもExcel2010でも同じように発生します。よって、Excel2003でギリギリ印刷する設定の場合はExcel2010ではずれる可能性がありますが、少し余裕をもたせたレイアウトの場合や自動でセル幅を広げる場合は、レイアウトのズレは気にしなくてよさそうです。

10. ファイルの保存先をCドライブの直下にしている場合は、変更させるほうが良いでしょう。特にWindows 7ではユーザのスキルレベルによっては動かないなどの苦情を聞くことがありそうです。よって、マニュアル等でマクロの配置先をユーザを納得させる必要があります。

最後に。

11. CommandBarsを使っているマクロについては検証していません。リボンインタフェースになったことで、もしかしたらそのままでは使えない可能性がありそうです。こちらは余裕ができれば別途検証してみたいと思います。

12. Office自体の操作性の違いについては、事前に違いが大きいことを説明してから、細かなことを説明し、バージョンが変わり大きく違うことになったと説明する方が良いでしょうか。先に細かなことを説明してから最後にバージョンが大きく変わって、操作性が変わったといっても納得されにくい気がします。説明の順序の問題ですが。
posted by Kiruahさん at 00:07| Comment(7) | TrackBack(0) | ノウハウ

2012年01月31日

Prototype.jsとjQueryの共存について

最近、私の周りで少しPrototype.jsとjQueryの共存について話題がありました。
ネットを見ると、当然たくさん対応方法があるのですが、私なりにも簡単にする方法を考えました。
とはいえ、このネタ自体、すでに古いものなので、さすがに今更もう情報として必要とする方もいないでしょうが、もしもの参考のために今までの方とは違うアプローチもあるんだなと思っていただければと思い、公開します。

多くのネットにあるアプローチは簡単です。
jQueryにが外部ライブラリと競合しないようにするための機能が提供されています。
具体的には下記のように、jQuery.noConflict();を利用する方法です。
このjQuery.noConflict()自体はjQueryオブジェクトを返却しますので、
下記のように$j等、適当な変数に格納します。これを$の代わりに利用します。

var $j = jQuery.noConflict() (function($) {

// jQueryの$を利用可能
});

もしくはもっとシンプルに、$を利用しないで、jQueryを利用する方法もあるでしょう。
こうすれば、function($)内では$をjQueryオブジェクトとして利用できます。

jQuery(document).ready(function($){

// jQueryの$を利用可能
});

これらの問題は、function($)の中でのみ有効であるという点です。つまり、その外でロードされるjQueryプラグインは書き換えが必要となります。
もしくは、PHP等を使って下記のように本体にインクルードする方法もあります。

var $j = jQuery.noConflict() (function($) {


});

これの弱点は、実行時には必ずPHPが必要で、ちょっとHTMLのレイアウトをローカルで確認するには不向きです。


私の考えるアプローチは2つです。

1. これからはjQuery一本でいく。よって、jQueryは$で、Prototype.jsの$を別名に変換しましょう
2. 動的にjavascriptファイルをロードしてしまおう

1. これからはjQuery一本でいく。よって、jQueryは$で、Prototype.jsの$を別名に変換しましょう
これは既存のPrototype.jsからhtmlから全て、"$("を"$p("に全置換してしまいます。
こうすると、今までのPrototype.jsの$()は全て$p()で利用することになります。
こうすれば競合しませんし、jQueryのプラグインもそのまま利用することができます。

これはPrototype 1.6.1でのprototype.jsファイルを何も考えず、普通に"$("を"$p("に全置換してしまって問題なさそうなことは確認済みです。
あとは、jQueryをまだ使っていないhtmlも同じように置換すれば、($ ()と記述されたりしていない限りは)リプレース可能です。

でも、やっぱり面倒です。

2. 動的にjavascriptファイルをロードしてしまおう

この方法はとても無理やりです。簡単にいえば、scriptタグを利用しないで動的にjavascriptファイルをロードしてしまいます。
先に見てしまうほうが早いでしょう。











jQuery.noConflict()のfunction($)内でloadJs()メソッドを呼び出します。引数にはロードしたいjavascriptファイル名を指定します。
loadJsの中ではjQuery.ajaxでjavascriptファイルをgetします。その時、dataFilterで取得したテキストの前後に以下を追加しています。

* 前 : _$=window.$;window.$=jQuery;
* 後 : ;window.$=_$;

前は、$はPrototype.jsの$ですので_$にバックアップし、$をjQueryオブジェクトにすり替えます。
その後、jQueryプラグインが動作し、終了後、バックアップした_$を$に戻し、Prototype.jsに戻します。先頭に;が付いているのは、最後に付け忘れとかないよう念のためです。

ついでにajaxでgetしてくる際、効くかどうか分かりませんが、一応キャッシュを有効にしています。ちょっと試した限りではよくわかりませんでしたが。。。

こうすると、scriptタグではありませんが、その代わりにloadJs()を利用し、jQueryで記述されたプラグインを、変更なしでロードすることができます。

ちなみにこのサンプルはダウンロードできるように準備して見ました。
サンプルでは、loadJs()関数は外出ししています。何の制約もかけない著作権も訴えませんので自己責任でご自由にご利用ください。
必要なjsファイルは全く入れておりませんが、ダウンロードしてみてみたい方はこちらをクリックして下さいませ。
私はこれでjQuery UIやその他star系、blockUI系のプラグインが正常動作するのを確認しました。

すでにカタがついている問題かと思いますが、調べた限り、私の気に入る回答がなかったので作って見ました。もしかしたらすでにもう、他の方がこの方法に到達しているかもしれませんが。。。
その時はごめんなさい。
posted by Kiruahさん at 01:00| Comment(2) | TrackBack(0) | ノウハウ

2011年06月21日

OutputStreamWriterやOutputStreamReaderの罠?

Javaにて、テキストファイルをオープンする際、最近ではUTF-8で記載したファイルも開くため、エンコーディングを事前に指定することも多いと思います。
本来は勝手にエンコーディングを見つけてくれると助かりますが、そういう処理も重たいですし、何よりプロジェクトにおいては共通化することがポイントだったりしますので、全体の決めでUTF-8とすることも多いでしょう。
もしかしたら、下記の内容、私のJavaの不勉強さで間違ったことを記載しているかもしれません。
その際はご容赦ください。


そういった場合に例えばPrintWriterを利用する場合、簡単には以下のように記載します。

PrintWriter pw = null;

try {
pw = new PrintWriter("ファイル名", "UTF-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}

このように実行した場合、PrintWriterのコンストラクタは以下のコードが実行されます。

public PrintWriter(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException {

this(new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(fileName), csn)), false);
}

※ 出典はJDK1.6 Update 26のソースコード(Copyright (c) 2006, Oracle)です。Oracle様、記載し申し訳ございません。

ポイントは、csnに変なエンコードを入れた場合、UnsupportedEncodingExceptionが出ることです。
この例外が出る前にはFileOututStreamの処理は成功しておりインスタンスは生成済みです。
その後、OutputStreamWriterのコンストラクタで例外が発生します。
その場合、FileOutputStreamのインスタンスはどうなってしまうのでしょうか。。。
普通はそのうち運がよければガベージコレクションが発動すると思います。
ガベージコレクションが発動すればfinalize内にclose()呼び出しがあるので、そこで確保されたリソースは解放されます。が、逆にガベージコレクションが発動されるまで開放されません。
何かしらのリソースをロックしてしまう可能性がありますので、注意する必要がありそうです。


普通は文字コードの設定などはプロジェクトの初期に行ってしまい、変更はしないものです。
ですが、ふとした作業(例えば変更してはいけないところを誤って変更していたなんてこともないとは言い切れないのが、この業界です)で、エンコーディングの指定を存在し得ないものにしていたなんてこともあるでしょう。通常はUnsupportedEncodingExceptionがスローされて、それをキャッチして異常終了させるとは思います。ですが、フレームワークによっては、そういった例外をもみ消して縮退運転的なことをさせることはないでしょうか?
それも普通はシステムの異常なのだから一度は止めてということもあるでしょうが、代替手段で行い、メインサーバは停止できないこともあります。私が関わっている案件はまさに止めるわけにはいかないシステム(止めてもいいけど、その後のリカバリが遅れたら〇〇億円とかになったらとか思うと。。。)です。。。

もみ消した場合は、ファイルがロックされたままだったり、利用しているStreamの種類によってはJNIの外でメモリリークも発生し、最悪はOutOfMemoryErrorとなり得ます。

なので、面倒なのですが私は必ずこのように記載するようにしています。


public void sample(File file, String encoding) {
PrintWriter pw = null;
BufferedWriter bw = null;
OutputStreamWriter osw = null;
FileOutputStream fos = null;

try {
fos = new FileOutputStream(file);
osw = new OutputStreamWriter(fos, encoding);
bw = new BufferedWriter(osw);
pw = new PrintWriter(bw);
} catch (Exception e) {
e.printStackTrace();
} finally {
close(pw, bw, osw, fos);
}
}

// 下記は本当はF/W側共通メソッドにしています。
public void close(Closeable... closeables) {

if (closeables == null) {
return;
}

for (Closeable c : closeables) {
if (c != null) {
try {
c.close();
} catch (Throwable e) {
}
}
}
}

こうすることで、実は例外発生時のスタックトレースも分割されるので、若干の処理速度とメモリ効率を犠牲にして、障害回復のしやすさを優先しています。
同じことはOutputStreamReaderでも言えると思います。

あとは、全てのJVMでfinalize()メソッドが確実に呼ばれるかどうか分かりませんので、確実にclose()するよう心がけておけばよいと考えます。
posted by Kiruahさん at 00:12| Comment(0) | TrackBack(0) | ノウハウ

2011年05月03日

設計書には業務理由を明記すべき

多くの設計書では、何をすべきかがシステム屋目線で記述されています。
例えば基本設計書、私のローカルな言葉では、「業務機能 機能処理フロー設計」、「業務機能 詳細設計」にあたりますが、これらは全て、なぜそれをしなければならないのか、顧客の業務の理由を明確に記述すべきだと考えます。

理由は主に下記4点です。
1. 顧客が機能の必要性を理解出来ないこともあります。
もしシステム開発に対して積極的な姿勢でない場合、レビューを実施しても疑問も感じずスルーしてしまいがちです。
その後、バグがあったとしても、お互いにレビューした/瑕疵だと強く主張することになります。
そういうリスクはなくなりませんが、多少は顧客にも機能の必要性を理解しやすくしたいと考えています。
2. テスト仕様書を作成する際に、業務上のあり得る/あり得ないも含めてシナリオを作成しやすくなります。
基本設計書フェーズでは結合テストやさらに上位のテストが実施されているはずです。
このタイミングではホワイトボックスやブラックボックスというよりはむしろ、より業務に近いシナリオベースでテストが記述されることも多いと思います。
よって、業務理由が記述されていれば、このシナリオの妥当性が分かりやすくなります。
3. 後任の担当者が理解しやすくなります
後から入ってきた担当者は業務も知らない、何も知らないケースが多く、それなのに担当に割り当てられます。
その際の教育負荷の軽減につながります。
4. 保守業務が多少楽になります。
あとから見たときに、なぜこのような実装になっているかがわかります。
よって、業務が変わっとしても実装の影響等が分かりやすくなります。

設計書への記述としては記述する範囲が狭くなりますが、2段組がベストです。
左に設計、右に関係する業務やなぜこうするのか、業務的な理由、システム的な理由、資料へのリンクや説明や議事録のパスなどが記述されているのが良いと考えます。
こうすると設計に時間がかかると思われます。しかし、その方があとの工程や引き継ぎが楽になります。
posted by Kiruahさん at 09:17| Comment(0) | TrackBack(0) | ノウハウ

2011年04月29日

私が考えているシステム開発の工程

システム開発をしていると、常に運用のことが後回しになっており、
作ってみたけれど、結局使いにくい、運用がまわしにくいと言われてしまうことが多いと感じます。
言い訳ではありますが、私はあまり運用設計まで実施することがないので、
常に最初から案件に関わっていればと思うこともあります。

会社では常に現行システムがあるのであれば、運用がどうなっているのか、性能や環境はどうなのかを
先に調査するよう求めています。
ですが、システムを作る人間は、どうやったらできるのか、どうやって作るのか、システムとはどうあるべきか、
方法論、方式論、開発論、手法ばかりが先行してしまいます。
私たちは作る側ですので、美しく、安く、それなりに品質の高いものをはやく作りたい、
それができれば評価される世界にいます。
たしかにその世界にいるので、上記の論述は必要です。
でもその前に、ちゃんと作ったあとのことを先に考えていたいと思うのです。

私だったらこんなシステム、結局面倒になって使わなくなるだろうなということを感じるものでも、
システム開発側のルールやリスク、場合によってはスキルや作ってしまったから等の理由で
顧客にリリースしてしまいます。
それでも私の会社でも納品さえしてしまえば、Win-Winの関係を築くことができたと言ってしまっています。
これは何を先にやるべきか、あまり明確になっていなからではないかと考えています。

よくあるV字モデルは、
要件定義、基本設計、詳細設計、製造、単体テスト、結合テスト、システムテスト、ユーザー受入テストと
だいたいこのような形で記述されています。
より詳細に見ても、要件定義や基本設計で運用の詳細を見てきた案件が一度もありませんでした。
他の方はもしかしたらやっているのかもしれません。
これは自分の今後のために、反省のためにやるべきことを明確にしていきたいと思います。


まだまだ考え中ではありますが、私が考えているシステム開発の工程は以下の通りです。
一部は詳細に表現しています。
これから、さらに細かく内容を確定していきたいと思います。

第1フェーズ
顧客の現状分析と要求確定
目的定義・実現内容(業務機能・確定可能な非機能)の確定
第2フェーズのスケジューリング

第2フェーズ
現行の運用・環境分析(調査)
実現可能性の分析
実現内容(未確定部分の非機能)の確定
新規 運用内容・運用環境(想定)・保守内容の設計
第3フェーズ以降のスケジューリング

第3フェーズ
業務機能 機能分割
業務機能 機能処理フロー設計
業務機能 詳細設計
開発環境 テスト環境 構築(手順策定)
システム・運用テスト設計
性能テスト設計

第4フェーズ
業務機能 共通処理の抽出
業務機能 機能処理フローをイベントドリブンに変換
フレームワーク設計・実装
新規 移行設計
結合テスト設計

第5フェーズ
フレームワーク テスト
実装
単機能テスト(単体テスト)

第6フェーズ
結合・トランザクション・性能テスト

第7フェーズ
システム・運用確認

第8フェーズ
導入
ユーザ確認


posted by Kiruahさん at 22:17| Comment(0) | TrackBack(0) | ノウハウ

2011年04月25日

システム開発工程

いろんな案件でシステム開発をしています。
どこも似たような感じで工程を踏んでいます。
いろいろな表現があるとは思いますし、間にこまかくあるところもあるでしょうが、概ねこのような感じではないでしょうか。

1. 要件定義 : 顧客の要件を確認する
2. 基本設計 : システムの概略についての設計を顧客に分かる言葉で表現する
この間にフレームワークの選定や開発が始まる
3. 詳細設計 : プログラム内部の設計を行う
この間にフレームワークのテストが始まる
4. 製造 : プログラム開発
5. 単体テスト : 開発したプログラム単品のテスト。だいたい製造と一緒
6. 結合テスト : 複数の機能の連結テスト
機能単位を含める場合や、規模によっては2つに分かれる
7. システムテスト : システム全体を結合してみてテスト
8. 受入テスト : 運用も含めて顧客が要求通りにできているか確認するためのテスト

でも、いつも最後のフェーズでいつも、運用に無理がきたりしています。
そうです、だれも使う人間のことなんて考えていません。
どうやって作るか、実現するか、何をシステムで機能を実現するか考えていますが、
だれがどうやって使うかは最後に決まっていませんか?

だから方式論、方法論、べき論ばかりが先行しているような気がします。
それが悪いわけではありませんが、それは我々SIerの自己満足にしかなりません。
これらは我々が品質をどうやって担保するか、安く作るかです。
もちろん、これは最終的には顧客が支払うお金に関係するためあるにこしたことはありませんし、ないのも悪いとは思います。
でもでも、それよりも本当に顧客のことを考えていますか?

私の妻は運用保守をやっています。話を聞いていて、いつも反省します。
多くのハードウェアベンダー、ソフトウェアベンダーの勝手な方式論でできる/できないという言葉に翻弄されてしまっているように聞こえます。つまり、私たちが本当はできても面倒だとか、我々が楽になるからとかが最終的に見え隠れしています。

結局、使えない、数年後にはさらに保守費を安くしたくなる、結局保守費は変わらない、結局フレームワークや作りがSIer独自で他の企業に頼れなくて縁を切れない、顧客もスキルが育たない状況です。
要は、顧客の無知に付け込んでいるようにいつも私ははたから見えているのです。
食べ物とかで無知に付けこまれて高いものを買わされると文句をいうくせに、いつも顧客との打ち合わせでは「顧客は分からないのだから」と言って、資料を用意したり隠したり。。。。。
本当は作れたものも、こちらの予算が回らなければ平気で「技術的には難しいです」なんてことも言います。
一部上場している大手SIerの全ては同じだと思います。なぜそれがわかるかというと、私の会社もそのひとつでしかも、SI専業でも5本指には入る大手です。。。

今、本当のシステム開発手順はどうあるべきか考えています。
これも完璧ではないとは思います。でも、もっともっと良いものがあると信じています。
こんなことばかり言って私は勝手で無責任の極みだとは思います。
でも、こうあるべきだと多少はよくなるというものを紹介出来ればと思います。
posted by Kiruahさん at 21:00| Comment(0) | TrackBack(0) | ノウハウ

2010年10月03日

Excelで作成した設計書を画面どおりに印刷したい

だいぶ仕事が落ち着いてきており、毎日余裕が出てきました。
うれしい限りですが、今使っているキーボードが身体にあわず、すぐにキーを打ち間違えてしまいます。さすがにドンキで特に何も考えずに買ってしまっただけあり、自分の中で反省です。

さて、会社でいろいろと仕事の種を作っているところではありますが、本業の案件では設計書やらテスト仕様書やらマニュアルやらを書いています。
どうしても都合上、WordではなくExcelで書かないといけないのですが、多くの人が悩んでいる通り、Excelは画面どおりに印刷されません。
日本人はExcelのセルを方眼用紙に見立て、DTPみたいな使い方を強要する謎の文化が存在します。Excelしか使えないと公言する方もいるぐらいです。。。私としてはワープロのほうが良いのですが。。。さらに言えば、TeXあたりがとも思ってしまいます。

さて、このExcel、ネットで調べてみると多くの意見はプリンタドライバと画面のドライバが違うからという意見が多いように見えます。これは分かりやすく説明してくれているのでしょうが、最初は「?」でした。ということで自分なりに色々と実験をしてみました。

結論から言えば、制限が若干あり、画質は落ち直接プリンタに印刷できず一旦PDF経由になりますが、見た目どおり印刷は可能です。

画面は画面のプロパティから設定される文字の大きさによって表示される基本サイズが変ります。画面のプロパティでは文字は96dpi(dpiは、1インチあたりに表示するドット数です。Windowsの画面ではこの値をベースにして画面の文字サイズが決定されます。96の値を変更して増やすと、文字が大きくなります。いろいろと違和感を感じますが、要はこの値をベースにしてExcelは画面を表示しています。

次にプリンタです。プリンタはプリンタごとにこのdpiが違います。そして、だいたいが300dpiからです。つまり、このdpiが違うことによりずれてしまうといえます。
試しに画面のdpiを300に変更してからExcel文書を作成し印刷してみてください。もしExcelの表示サイズが100%であれば画面の見た目どおり印刷されるのではないでしょうか。ただし、ディスプレイによってはダメかもしれません。

ですが、画面の見た目を300dpiにすると文字が大きすぎて使いにくい状態です。よって、プリンタのほうを96dpiにしてあげると良い感じになります。もちろん、プリンタはだいたい300dpiからですので、

1. そもそも設定を変更できない
2. 設定できても画質が悪そうだ(これは設計書メインならば無視できます)

という課題があります。2はおいておいても1は何とかしたいところです。ということで、いくつか対応方法を考えました。

a. PDF出力するソフトウェアで何とかできないか
b. プリンタドライバのdpiを変更できないか
c. プリンタドライバを自作する

結局一番手軽だったのは、aとbのPDFプリンタの設定ファイル変更を組み合わせるものです。
今回はその方法を紹介します。が、システムが動作しない、プリンタが動作しなくなった等が発生したとしても、私は一切責任をとりませんので、了承できる方のみ続きをお読みください。

今回利用させていただいたのはCubePDFです。このソフト、どうやら中はGhostScriptを利用しているのですが、すぐに最新の9.00になっていました。 Bullzip PDF Printerでも良いのですが、こちらはまだ9.00ではなく、見た目がきれいにならないので変更しました。
無償で更改してくださっているCubeSoft様、ありがとうございます。

ちなみにCubePDFをインストールしますが、CubeToolbarは入れていません。

その後、Windows\System32\Spoolフォルダのさらに子供のフォルダにCUBEPDF.PPDというファイルがあります。どのフォルダにあるかはOSによって違いますので、ここでは割愛します。
このCUBEPDF.PPDというファイルをこのファイルに置換します。ちなみにこれは勝手に書き換えておりますので動作保証外かと思います。

これで、ある程度好きなdpiをプリンタドライバから選択できます。
画面のプロパティのdpiと同じ値を設定して印刷してみてください。
どうでしょうか?ちなみに画面の表示を100%にしているかなどでもずれたりするので、根本的にはやはり難しいものがあります。。。

ちなみに、印刷範囲を改ページプレビューで変更している場合、おそらくページ設定の拡大縮小印刷の%が100ではないと思います。
その際は、[画面のdpi] * ([Excelの拡大縮小値] + 1) / 100の値をdpiにすると、印刷範囲はずれてしまいますがだいたい一致するようになります。

できれば、Excelがきれいに出力できるようにオプション変更できるとうれしい限りですね。
posted by Kiruahさん at 00:10| Comment(0) | TrackBack(0) | ノウハウ

2009年10月25日

MS-AccessのVBAでファイルを開くダイアログを表示させたい

MS-AccessのVBAでファイルを開くダイアログを利用する場合、通常はあまり拡張性のない命令を利用することになります。
検索してみると、MS-Accessでは非公開命令により汎用的なファイルを開くためのダイアログを表示させる機能があります。

いろいろな方が実行方法を見せてくれていますが、そのまま貼り付けて利用可能な形でまとめてみました。

' ファイルダイアログを表示し、ファイルを選択させる
Function ShowOpenFileDialog(Optional ByVal appTitle As String, Optional ByVal dialogTitle As String, Optional ByVal initialFileName As String, Optional ByVal initialDir As String, Optional ByVal filter As String, Optional ByVal filterIndex As Integer, Optional ByVal openType As Boolean) As String

Const ENABLE_WIZHOOK = 51488399
Const DISABLE_WIZHOOK = 0

Dim fileName As String
Dim result As Integer

On Error GoTo Err

fileName = initialFileName

If filter = "" Then
filter = "すべてのファイル (*.*)|*.*"
End If

WizHook.Key = ENABLE_WIZHOOK
result = WizHook.GetFileName( _
0, appTitle, dialogTitle, "", fileName, initialDir, _
filter, _
filterIndex, 0, 0, openType _
)
WizHook.Key = DISABLE_WIZHOOK

GoTo Finally

Err:
fileName = ""
MsgBox "エラーが発生しました。" & vbCrLf & vbCrLf & "エラーコード:" & Err.Number & vbCrLf & vbCrLf & Err.Description

Finally:
ShowOpenFileDialog = fileName

End Function

result変数を削除しても良かったのですが、一応デバッグしやすくするために残してあります。不要な方は削除してください。
posted by Kiruahさん at 11:29| Comment(0) | TrackBack(0) | ノウハウ

2009年10月10日

Javaで実行時にクラスパスを追加する

会社で単体テストを実行する場合、構成管理上特殊なディレクトリ配置をとっています。そんな特殊な方法をとらなくてもと思う方もいるかもしれませんが、具体的にはお見せできないのですが、現状の方法は人の開発環境に依存せず、だれがいつでも、どこのパスにsvnからチェックアウトして実施しても実行ボタンを押すだけでテストを実行させることが出来ます。これにより、初心者や新規参画者がテストを始めるまでの時間がこれまでかかっていたのですが、環境の調整を意識しなくてもテストができるため、保守効率が高くなっています。
ついでに言えば、テストプロジェクトにテスト実行用のデータを単体テスト用に配置することも無く、実行前に単体テストに必要な情報を事前にコピーしてきて利用するようになっています。
これによって、単体テスト用の設定ファイルとリリース用の設定ファイルに差異が出てしまうという事態を避けています。
データを一元管理し、正しいデータの取得はここだけを見ればよい、他は捨てても良いとしています。

これは私が考える理想の単体〜総合テストを実現する上での、まずは第一歩のようなところであります。今は仕事をやりながら、その理想のテスト方式を少しずつ導入しているところです。
(さすがに一気に変えると反発もありメリットを想像できないみたいなので、少しずつ導入しメリットを体感させて、より良くしている最中です。)

そんな中で、テスト実行時にテストプログラムが、動的にクラスパスを変更できるほうが環境依存しないケースがあることに気づきました。
ですが、Javaでそんなことできるのかしらと思いましたが、調べてみるとやはりpublicな方法はないようです。

やるとすると、以下のようにリフレクションからprivateメソッドを呼び出し、直接クラスパスをリストに追加させる方式しかないようです。

/**
* クラスパスを現在のシステムクラスローダに追加する
*
* @param classPath 追加したいクラスパス
* @throws Exception システム例外
*/
public static void addClassPathToClassLoader(File classPath)
throws Exception {

addClassPathToClassLoader((URLClassLoader) ClassLoader.getSystemClassLoader(), classPath);
}

/**
* クラスパスを指定したクラスローダに追加する
*
* @param classLoader クラスローダ
* @param classPath 追加したいクラスパス
* @throws Exception システム例外
*/
public static void addClassPathToClassLoader(URLClassLoader classLoader, File classPath)
throws Exception {

Class classClassLoader = URLClassLoader.class;

Method methodAddUrl = classClassLoader.getDeclaredMethod("addURL", URL.class);

methodAddUrl.setAccessible(true);
methodAddUrl.invoke(classLoader, classPath.toURI().toURL());
}

これを使う場合は、JREがシステムのクラスローダにURLClassLoaderを継承したクラスローダを利用していることが前提となりますのでご注意ください。
もし、ClassCastExceptionがスローされる場合は、別の方法を探すことをおすすめします。

使い方は以下のような感じになると思います。

addClassPathToClassLoader(new File("〜.jar"));

今即攻で覚えている範囲でテキストエディタで書いたのみのため、コンパイルもしていません。会社で使っているコードとも少し違って、シンプルになっています。
もしエラーがありましたら、気づいたら修正しますが、独力で修正しご利用ください。
posted by Kiruahさん at 15:18| Comment(0) | TrackBack(0) | ノウハウ

2009年09月13日

Javaでカレントディレクトリを変更する

Javaを利用していて、カレントディレクトリを変更したいという方は多いと思います。
しかし、現状のJavaでは基本APIだけではカレントディレクトリを容易に変更することはできません。

ネットで検索するなどして良く出る方法としては以下の3つがあります。

1. user.dirの値を変更する
2. コマンドプロンプト経由でcd(等のOS依存)コマンドを実行する

が、上記方法では変更できません。
対応方法としては以下の2方法で対応可能です。

3. Win32 APIのSetCurrentDirectory()等、OSのAPIをラップしたライブラリをC言語等で作成する
4. 4のようなAPIをすでにラップしたライブラリを利用する。


以下では、動作しない手法について調査してみた結果を記載します。

1. user.dirの値を変更する
1の方法は以下のAPIを利用することで変更できます。

System.setProperty("user.dir", ディレクトリ名);

この方法では、File(".")のディレクトリは変更されます。
しかしながら、取得対象のファイルは変更されません。

以下のようなサンプルプログラムを作成し確認すると分かりますが、
全てカレントディレクトリが変更されずにファイルの内容が出力されます。

import java.io.*;

/**
* user.dirのサンプルテスト
* カレントディレクトリにtest.txt、子ディレクトリ newcd、
* さらにnewcd配下にtest.txtを配置してください。
*
* @author Kiruah
*/
public class SampleUserDir {

/**
* メイン処理
*
* @param args 引数
*/
public static void main(String[] args) {

testUserDir();
}

/**
* user.dirのテスト
*/
private static void testUserDir() {

// 現在のuser.dirの値を利用してテスト
System.out.println("現在のuser.dirの値:" + System.getProperty("user.dir"));

File fileBeforeChange = new File(".");

System.out.println("現在のFile(\".\")が示すディレクトリ:" + fileBeforeChange.getAbsolutePath());
showFile(new File("test.txt"));

// user.dirの値を変更してテスト
System.setProperty("user.dir", System.getProperty("user.dir") + System.getProperty("file.separator") + "newcd");

File fileAfterChange = new File(".");

System.out.println("変更後のFile(\".\")が示すディレクトリ:" + fileAfterChange.getAbsolutePath());

showFile(new File("test.txt"));
}

/**
* ファイルの内容を表示
* 引数をStringに変更しても同じ結果になります。
*
* @param file 表示したいファイル
*/
private static void showFile(File file) {

BufferedReader br = null;

try {
br = new BufferedReader(new FileReader(file));

System.out.println("File:" + file.getAbsolutePath() + "の内容");
System.out.println(br.readLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
}
}
}
}
}

この方法では、new File(".")の位置が変っても、java.io.〜系のファイル取得位置までは変らないためカレントディレクトリ変更による意図した期待結果は得られません。

2. コマンドプロンプト経由でcd(等のOS依存)コマンドを実行する
この方法は、実行時にOSのコマンドを利用してディレクトリを変更しようとする方法です。
ですが、この方法でもディレクトリは変更できません。

この方法では、例えば以下のようにJavaプログラム中からcmd(Windowsのコマンド)を経由してcdコマンドを発行しています。
cmdのオプション/Kは、cdコマンドの実行が終わっても、念のためcmdが終了してしまわないようにしています。
実行すると分かるとおり、ディレクトリは変更されていません。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

/**
* OSのコマンドを利用する場合のサンプルテスト。
* サンプルはWindowsの場合。
*
* @author Kiruah
*/
public class SampleShell {

/**
* メイン処理
*
* @param args 引数
*/
public static void main(String[] args) {

testUserDir();
}

/**
* user.dirのテスト
*/
private static void testUserDir() {

showFile(new File("test.txt"));

try {
Runtime.getRuntime().exec(new String[] {"cmd", "/K", "cd", "newcd"});
} catch (IOException e) {
e.printStackTrace();
}

showFile(new File("test.txt"));
}

/**
* ファイルの内容を表示
* 引数をStringに変更しても同じ結果になります。
*
* @param file 表示したいファイル
*/
private static void showFile(File file) {

BufferedReader br = null;

try {
br = new BufferedReader(new FileReader(file));

System.out.println("File:" + file.getAbsolutePath() + "の内容");
System.out.println(br.readLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
}
}
}
}
}

この理由は単純です。このJavaのプロセスと、Javaから実行したcmdは異なるプロセス(子プロセス)になります。よって、子プロセス上でいくらディレクトリを変更したところで、別のプログラムとして認識されているのですからJava側はディレクトリは変更されません。

3. Win32 APIのSetCurrentDirectory()等、OSのAPIをラップしたライブラリをC言語等で作成する
この方法が一番現実的ではありますが、一番難易度が高い方法です。
OSのAPIを利用してディレクトリを変更します。よって、確実に変更できます。ただし、new File(".")とは同期がとれませんので、user.dirの値も同時に同じにしておくと良いと思います(user.dirの値はJava起動時に決定されるため、プログラム実行中は自動的に変更されません)。

この方法は、OSによってC言語側で記述する内容が異なります。またJNIを利用する必要もあり面倒です。
Windowsの場合の作成手順としては、
1. C言語でJNIとWin32 APIを利用してSetCurrentDirectory()を呼び出すプログラムを作成する(DLLで作成すると良い)。
2. 1のプログラムをコンパイルする。
3. Java側で2のライブラリをロードするJavaプログラムを作成し、ロード後、C言語のプログラムを実行する。

この方法については詳細を省略しますが、実現は可能です。

4. 3のようなAPIをすでにラップしたライブラリを利用する。
3のような方法は以外に面倒ですが、Windowsに限っては実はこのAPIをラッピングしたライブラリがあります。
そのライブラリを利用することで、ディレクトリの変更を実現できます。

SWT WIN32 EXTENSIONというSWTライブラリの拡張を利用します。
このライブラリはEclipseでも利用されているSWTのうち、Windows版のみを本当に拡張したライブラリという位置づけです。
このライブラリではUNIX等ではディレクトリ変更できませんので、ご注意ください。

また、このライブラリはSWTを必要とします。SWTを別途ダウンロードする必要がありますので、その点も注意が必要です。
ただし、Eclipseを利用している方はEclipseの中にすでにSWTのjarファイルがあります。

1) SWTを配置します
Eclipseのpluginsフォルダから以下のようなorg.eclipse.swt.win32で始まるファイルをコピーし、
プロジェクトに配置、ビルドパスに通してください。
org.eclipse.swt.win32.win32.x86_3.5.0.v3550b.jar

2) SWT WIN32 EXTENSIONをダウンロードし、以下のファイル(このファイル名はバージョン1.0.5のものです。ファイル名は今後のバージョンアップで変更される可能性があります。ご注意ください)をビルドパスに通します。
org.sf.feeling.swt.win32.extension_1.0.5.v20081205.jar

3) あわせて、swt-extension-win32.dll ファイルをプロジェクトのカレントディレクトリかSystem32フォルダなど、
PATHが通っているディレクトリに配置します。

4) 以下のようにFileSystem.setCurrentDirectory()を実行することで、ディレクトリを変更できます。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import org.sf.feeling.swt.win32.extension.io.FileSystem;

/**
* SWT WIN32 EXTENSIONを利用した場合のサンプルプログラム
* サンプルはWindowsの場合。ご利用は自己責任でお願いします。
*
* @author Kiruah
*/
public class SampleSwtWin32Ext {

/**
* メイン処理
*
* @param args 引数
*/
public static void main(String[] args) {

testUserDir();
}

/**
* user.dirのテスト
*/
private static void testUserDir() {

showFile(new File("test.txt"));

FileSystem.setCurrentDirectory("newcd");

showFile(new File("test.txt"));
}

/**
* ファイルの内容を表示
* 引数をStringに変更しても同じ結果になります。
*
* @param file 表示したいファイル
*/
private static void showFile(File file) {

BufferedReader br = null;

try {
br = new BufferedReader(new FileReader(file));

System.out.println("File:" + file.getAbsolutePath() + "の内容");
System.out.println(br.readLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (Exception e) {
}
}
}
}
}

この場合ですと、正しくディレクトリが変更され、newcdディレクトリに配置してあるtest.txtファイルの内容が表示されます。
ただし、jarやdllなどのおまけが付くため、若干利便性は劣るといえます。


【まとめ】
Javaでは標準ではカレントディレクトリの変更方法は、現在のところ提供されていません。
それは現在のバージョンではという話です。将来的にはバージョンがあがり、そういう機能も提供されるかもしれませんが、
現在のところはOSに依存した(Javaではない)APIを利用する方法しかありません。
ただし、Windowsは自作せずともライブラリが存在しますので、そういったライブラリを探して利用する方法があります。
他のOSでも有志の方が作成されているかもしれませんので、探してみると良いと思います。

余談ですが、SWTもSWT WIN32 EXTENSIONも、中身を見るとかなりのWin32 APIがラッピングされていることに気づきます。
これだけのことができれば、相当いろいろなことができます。
が、下手に利用するとシステムを破壊しかねないケースもあると思いますので、利用の際はご注意ください。
ちなみに、申し訳ありませんが、私は責任は取れませんのでご注意ください。
posted by Kiruahさん at 12:51| Comment(0) | TrackBack(0) | ノウハウ

2009年06月27日

プログラムを作成するときに発揮する論理(ロジカル)力について(3)

3. スタート地点とゴールまでの通過ポイントの通り方は最適か?

よく人に説明する場合、ある程度手順ではありませんが、話のつながりを持たせて話さないと、「あれ?今の関係していたの?」や「それは関係ないんじゃないの?」と言われてしまうことが多いと思います。
実際には関係しているつもりでも、やっぱりそれは暗黙のうちに相手も分かったつもりで話しているということもあると思います。その場合には1での暗黙のうちに手順を飛ばしてしまうケースが生じているためと言えます。

同様に、人に説明しているとだんだん説明の内容が矛盾してきてしまったり、話が前後してしまうことはありませんか?
それは前の話の内容と、後の話の内容が明確でないケースでも発生しますが、話す順番が良くないために、自分で混乱してきてしまい、当初思っていた想定とは違うことになっていることは、結構起こりやすいと思います。

これがプログラムを作るときに起きた場合どうなると思いますか?
良いケースではプログラムが思った通りの結果を出さないことになります。つまり、当初こういう結果を出そうと思っていたのに、気づいたら途中からおかしな計算をしていた等のケースです。話が前後した場合での分かりやすい例で言えば、 3 + 4 × 2 を計算する場合、通常であれば順序は4 × 2を最初に計算しないといけませんが、気づいたら3 + 4を先にやってしまっていたということに近いと思います。

悪いケースでは、プログラムが暴走、つまりプログラムが終わらないケースが発生します。
このようなケースは途中から論理が少しずれてしまっている場合が多いと思います。少し宜しくない例えですが、「お金が欲しい」→「お金がないからATMからお金をおろしたい」→「お金をATMからおろしたいけれど、口座がない」→「口座を作るには、最低1000円はお金が必要だ」→「お金が1円も無いから口座が作れない」→「お金が欲しい」と言う様に、最初に戻ってしまうケースです。
これは、ぱっとみ論理的にはおかしくないようにも見えますが、現実的には変だと思いませんか?
お金がなければATMからおろしたいというのは、この話の前提に口座を持っていて、そこにお金がなければならないのに、その前提にふれず、かつそれがあたかも前提としてあるようにATMからおろすに話を持っていっています。
これはまず、「お金が欲しい」→「お金はどうしたら手に入るか、1) 口座を持っているか? 2) 働くか?」などのように、その前提条件をチェックする等の手順が必要になりますし、その手順がちゃんと途中に存在する必要があったりします。

話を論理的にする、プログラムを動作するように作るには、1での話にあった暗黙の手順を明確にしたり、2での話にあったスタート・ゴールの明確化のほかに、スタート・ゴールまでの手順をどのようにつなげるかがとても重要です。
これが正しく出来なければ、話がだんだんずれていって、ゴールからかけ離れたりして、相手に伝わらなくなります。


話は分かりますが、実際にはこんなことを考える機会はなかなか無いように思います。
そういう場合では、私は例えば、買い物をするときにどういう順番、経路で行けば最も効率的で速いかを考えて出かけるというのが分かりやすいと思います。

例えば、今はすでに図書館等にいます。これから家まで買い物したり、銀行へ行ったりしたいとします。

現在の所持金、3000円。途中で買い物等のため、立ち寄りたいと考えています。
A) 図書館 : スタート地点
B) 薬局 : 1000円の買い物。購入後は500gだけ荷物が増える。10分ぐらいで終わり。
C) スーパー : 2000円の買い物。購入後は2kg荷物が増える。15分ぐらいで終わり。
D) 本屋 : 1500円の買い物。購入後は1kg荷物が増える。20分ぐらい立ち読みし、購入。
E) 銀行 : 5000円お金を引き出す
F) 家 : ゴール

等のように考えて、地図の上でいろいろな位置にA〜Fを配置してみてください。
実際の近所の地図を使ったり、仮想的に地図を作り、一方通行などを作るのもおもしろいと思います。
先にスーパーで買い物をすると、重たいものを持ったまま立ち読みになるのでつらいから後に回すこともあるでしょう。しかし、家に最も近ければ先に行くのもありです。
自分がどうしたいのか、それは時間的に速い、歩く距離が短い、荷物が重くないようにしたい等の条件から最適なものを見つける癖をつけると良いと思います。
他には銀行でお金をおろさなければ、買い物を継続できませんので、その分も考慮する必要があって難しいです。


最適なポイントの通り方は、日常生活でも例えば行き方、勉強の仕方、朝の準備の手順等、最も効率的に行うにはどうするかを考える癖がついている方が、やはり論理的にプログラムできるような気がします。これは、勉強するよりも日常的に考えるほうが分かりやすいと思います。

なかなか大変ですが、一度一つ何かのやり方は果たして最適か?そしてそれを最適化してみようとやってみると面白いと思いますが、いかがでしょうか?
posted by Kiruahさん at 09:05| Comment(0) | TrackBack(0) | ノウハウ

2009年06月26日

プログラムを作成するときに発揮する論理(ロジカル)力について(2)

2. スタート地点とゴールは明確か?

プログラムを作る場合は、何を作るか明確でなければ作ることができません。
確かにある程度作りたいものがあれば、それなりのものができます。
また、作りたいものがまだ漠然としているから、ある程度のものにし、作っている途中で
明確にすることもありますが、それは途中経過が明確であり、また最終的にはゴールも明確になります。
よって、作りたいものを明確にする必要があります。
が、同様にして現状も明確にする必要があります。


例えば、あなたが先輩から「飲み物を買ってきて欲しい」と頼まれたとします。
通常であれば、たとえどんな人でもこれで買いに行くことはできないですよね。
これで行ってきますと言ってしまう人は、なかなかの怖いもの知らずです、きっと。

では、あなたは飲み物を買ってくるという依頼を遂行するために、他にどんな情報を
聞かなければならないでしょうか?
普通ならば、以下のような質問をすると思います。

1) どんな飲み物を買ってくるか?
2) 1)に関連し、銘柄や味・サイズは?
3) もし無ければ、代替案は?
4) いくら位までに抑えたいか?
5) どこで買ってくるべきか?
6) お金の支払いは?

これで大体の人は買いに行くのではないでしょうか?
ですが、実はこれだけでは本質的には先輩の要求を満たせない可能性があります。
本当は暗黙のうちに決めてしまっている・分かりきっているか、忘れている質問がまだ隠されています。

7) いつまでに(どれくらいの時間で)買ってこないといけないか?
8) 戻ってきたときに先輩がどこにいるか?
9) 8)に関連して、先輩がいなかったときにどうするか?
(探せばまだありそうですが、この辺で)

この質問に対して、自分の現状を知らなければならないと言えます。

7') 今は何時なのか? よって、何時までに戻らないといけないか?
8') 今の場所、お店の場所、戻るべき場所はどこなのか? 行ける距離か?
9') 先輩がいないときの対応は取れるか? 例えば冷蔵庫に入れてと言われても
冷蔵庫がなければ対応できない
検討事項) そもそもこれらは実現可能なのか?

このように暗黙のうちにゴールの条件を決めてしまうか確認しない状態、つまり明確ではない状態に
なると、ロジカルに物事を考えられません。つまり、手順を明確にできません。
7〜9)は確認しなくても買いに行くことはできますが、最悪の状況が発生して買ってきたら先輩が外出していたでは
先輩の依頼は達成できませんよね。
同様にその7〜9)を聞く上で、自分の現状が分からなければ実現できるのかどうか分かりません。
また、実現できない可能性も判断できず、回避策も立てられないでしょう。

つまり、論理力には以下のことが求められます。
1. 細かいゴール(目的・理由)の内容(要件)を明確にする
2. ゴールだけでなく、スタートも明確にする

これは目標を立てるときと同じです。自分の将来(ゴール)と、未達成の今の状態(スタート)を明確にしているからことs、
ゴールへ行くための道筋が見えて、適切な目標達成手順を考えることができるのです。


プログラムを作る場合で例えれば、先輩が利用者であり、あなたがプログラムの開発者になります。
何を作りたいのか、作りたいものが明確でなければ、作ったものは最悪無駄になります。
このとき、まず最初に聞くべきゴールの内容とは、目的(理由)です。
つまり、これを誰が何のために、どんなメリットがあって使いたいのか?が重要です。
それを踏まえなければ、どういう画面にすればよいか、どこまで機能を提供してあげればよいかは
決められません。それを勝手に決めるという事は、もしかしたらメリットを教授できない
システムになってしまうかもしれません。

そうならないためにもスタート地点とゴールの状態は明確でなければなりません。
ただし、スタートとゴールの間にもいくつか通過ポイントを立てます。
それも小さな範囲で見たらスタートとゴールになります(通過ポイントから通過ポイントなど)。よって、通過ポイントを立てたときも、通過ポイントを明確にする必要がありますので注意してください。
posted by Kiruahさん at 00:43| Comment(0) | TrackBack(0) | ノウハウ