社員ブログ
HOME  社員ブログ

アーカイブ

タグ付きの投稿 ‘オブジェクト指向’

オブジェクト指向プログラミングと関数型プログラミング – 最適な組み合わせは何か?

2015年01月19日 0時16分33秒

こんにちは、プリーストです。

個人的な出来事ですが、去年の夏にSoftware Designの2014年9月号のきしださんの記事を立ち読みして衝撃を受けました。

SoftwareDesign9月号でオブジェクト指向の記事を書きました

詳しくは覚えていないけれども、現在の技術動向はオブジェクト指向プログラミングを正しく使えば常に最適解が得られるのというのは幻想で、関数型プログラミングやステートレスなアーキテクチャの方が最適解を得られることが多くなってきているという話だった気がします。

関数型プログラミングについては、Scalaの研修でコレクションの処理や大量データを並列に処理する場合にはシンプルに記述できるなぁという程度の理解でした。 オブジェクト指向プログラミングとは絶対的な思想ではなく、単にシステム要件や扱うデータをソースコードとして整理する時に綺麗に整理できる技術、という理解を自分もしていて、そこはきしださんに共感できました。

衝撃を受けたのはシステムに状態を持たない事が最適というケースが増えてきているということでした。Webシステム実装の動向などについて知り合いの開発者からセッション管理についてのことなどをちらっとは聞いていましたが、きしださんの記事では保守性や変更に対する柔軟さも言語機能で複雑に共通化して管理するより、TDDやCIなどを実現するミドルウェアやツールで改修時の品質を担保した方が効率が良くなってきているとのことたっだと思います。

その記事を読むまでは、顧客要求に無関係の開発・保守効率のみを考慮した場合の技術的な最適解はクラスを用いてドメインを正しく設計すること、RDBなどのデータストアのモデルとシステム内ドメインモデル更にUIのデータ構造間の変換を適切な粒度で共通化しコントロールすることと考えていたので、きしださんの記事を読んでから最適解を判断する時の軸が分からなくなっていました。 その時は最終的にはドメイン駆動開発的に業務で使用するデータモデルとシステム内ドメインモデルを同期させ続ける方向とは別の最適解があるらしいとぼんやり理解して半年経った今もそれはいまだに分からないままです。

ただ、最近では何が分からないかがなんとなく分かってきた気がします。

オブジェクト指向プログラミング以前のシステムの組み方は、要求された処理を階層的に分解して整理する構造化プログラミングというやり方でした。それだと処理は整理できますがシステムの状態(変数)が管理できずに改修時に影響する変数を使用している箇所を全て洗い出して調査し、全ての箇所を新要件に合わせて改修しかつ既存のシステム要件に影響を与えていないか網羅的にテストをする必要がありました。現在の開発現場でも残念ながらほとんどいまだにこの苦労をしている現場が多いのが現状です。

オブジェクト指向プログラミングが目指したのは、処理を分解して整理するのみではなくシステムの状態(変数)をオブジェクトに閉じ込めてオブジェクト同士のネットワークとしてシステムを構築することでシステムが持つ状態(変数)の定義変化に対する影響範囲を限定しシステム全体の見通しも良くするということだと理解しています。

それと比較して関数型プログラミングのパラダイムは処理を関数として分解して整理することは構造化プログラミングと同じですが、構造化プログラミングとは異なり関数型言語自体がそもそも状態をシステムに持たないことを前提として設計されています。UIやDBは状態を持ちますが、UIからDB間の処理の時に使用するデータは引数として受け渡すということだと思います。 それならUIをVBで設計して処理は全てSQLで書き下すような設計と大差ない気がしますが、恐らく状態の変化を禁止するという関数型言語の特性と関数型言語が持つ表現力が、状態を管理せずとも見通しが良くなることを実現可能にするんじゃないかなと思います。

システム要件をUI設計及びシステムのソースコードとDB構造として抽象化する時に、インプットとアウトプット間の状態変化を禁止し処理を関数のネットワークとして整理することが最適解となるかもしれないということかなと想像しています。そういうことができたら状態を持たないことを強制することができ確かにテストや管理は格段に楽になる可能性があります。

そう考えた時に、ではなぜオブジェクト指向プログラミングではシステム内でモデルを定義して状態を保持する必要があったのか、まだきちんと理解していない事に気が付きました。 この問題は今までもトランザクションスクリプトとしてユースケースやユーザのアクション毎に処理を分けて書き下すか、ドメインモデルとして要件が必要とするデータ構造をシステムが扱いやすく見通しやすく抽象化してモデルに対して処理を記述していくかという選択の問題として存在していた気がします。 実際の現場でも、各画面毎に意識的に共通化せずに処理を分けているようなシステムがあり、それはそれで大規模なシステムを大人数で分業する場合には影響範囲がシンプルで管理しやすいなと感じたりしています。 関数型プログラミング的な書き方とオブジェクト指向プログラミング的な書き方を使い分ける時になぜシステム内にモデルが必要だったのかを理解することが必要だと感じています。 システムで処理しやすいデータ構造の定義とシステム内でのデータの保持がいったいどのケースで有効でどのケースで不利となるかということでもあるかなと。

もうひとつ分からないのは単なる勉強不足ですが、関数型言語の機能です。関数型言語が備える機能が、手続き型言語で単に状態を持たせずに実装した場合と比較して恐らくとてもシンプルに見通し良く整理できるのだと思います。その機能の概念と意義を理解しないと使い分けられないなと思いました。

それから、ビッグデータを扱う場合などに使用する非構造型データストアや分散型のプラットホーム、ブラウザでのHTML5などによる最新のデータ表現方法、最新のWebでのセッション等の状態管理の方法など、どこでどういう状態(変数)の変化と保持が必要なのかもしくは不要なのかが分からないと判断できないと感じます。

今、ようやく分からないことが見えてきて、まぁ僕は全く分かっていなくて勉強不足なことが改めて自覚できてなんとなく面白くなってきました。 本当に刺激を与えてくれたきしださんには感謝したいです。

やはり、JavaやC#、Rubyといった汎用プログラミング言語から更に抽象化を進めて効率化するにはケースバイケースの選択が必須なんだなーと感じております。

顧客要求やシステム要件を抽象化する時に、システム構造、プログラミング言語、フレームワーク、プラットフォーム、開発環境などに加えて、どのパラダイムで実装すべきも顧客要件や機能、システムの種類や機能・処理の種類毎に使い分ける必要が出てくるということです。 選択肢が広がってますます難しくなりますが、開発・保守を楽にする為にいろんな人が新しい技術を開発し選択肢が広がっているはずなので、今後はプログラミングの実装自体のスピードよりも、きちんとした技術概念の理解と顧客要求や開発要員スキルの理解に基づいて適切な判断と選択ができるかどうかが、楽に開発できるかどうか開発コストがどれだけかかるかをますます左右していくんだなぁと予想しています。

まずは、きちんときしださんの記事を読み返すところから始めようかな。

それではまた♪

社員:プリースト, 開発言語 , , ,

VB6によるアーキテクチャ設計 その2 - エラー処理と関心事の分離

2014年08月27日 6時57分50秒

こんにちは。プリーストです。

前回の投稿からやや時間が空いてしまいましたが、前回に引き続きVB6での開発のお話です。

前回は、主にシステムの前提制約とVB6の特徴、そしてプロジェクトの分割と共通クラスを用いてポリモーフィズムを使用した時のように、該当プロジェクト固有の値を使って共通クラスで処理させる方法を書きました。

VB6によるアーキテクチャ設計 - VB6でオブジェクト指向プログラミングはどこまでできるか?


今回は、以下のVB6の特徴の4番を使用したエラー処理について主に書きたいと思います。

○VB6の特徴(実装中に気づいたもの)
1. クラスを定義できる。ただし、提供される機能はカプセル化のみで継承、ポリモーフィズムはサポートされない。
2. モジュールには変数と静的なプロシージャのみ定義できる。静的クラスのような使い方ができる。
3. Formはクラスであり、newしてインスタンスを生成できる。インスタンス化しなくても使用できる。
4. try~chatchは無いが、On Error~を用いることで例外補足が可能。
5. 自分で定義した構造体をプロシージャの引数に取ることはできない。
6. 要素数0の配列を定義できない。
7. コレクションが使用できる。速度は配列より速いとのこと。
8. コレクションはJavaのジェネリクスのように要素の型を指定できない。コレクションを引数にするとプロシージャ側から見ると何が格納されているかは保証されない。
9. 10進数の計算で精度が必要な場合には、Variant型の変数にCDec関数で数値をDecimal型に変換して格納する。Decimal型の変数は定義できない。


VB6でのエラー処理は一般的に、

Private Sub subXxxxx()

    On Error GoTo ErrorHandler

    ‘ エラーが発生する可能性のある処理
        
  Exit Sub

ErrorHandler:
    ‘ メッセージ表示などのエラー処理
    
End Sub

等と書きます。
しかし、エラー処理をプロシージャ毎に書いていたのでは記述が冗長なばかりか、エラーハンドリングに変更を加えたい場合に工数が膨らみます。
そこで、エラー処理を共通化する試みをしました。方法は単純で共通のプロシージャを用いるということです。


○全プロシージャ共通の実装イメージ
Private Sub subXxxxx()
    On Error GoTo ErrorHandler

    ‘ エラー情報クリア
    Call subClearError
        
    ‘ エラーが発生する可能性のあるサブプロシージャ
    Call subYyyyy()
    If gobjError.ErrorCode <> “” Then GoTo ErrorHandler
     :
     :
     :   
ExitHandler:
    Call subEndProcces
    Exit Sub

ErrorHandler:
    Call subErrorProcces
    
End Sub


○エラーを発生させたい処理
Private Sub subYyyyy()
    On Error GoTo ErrorHandler

    ‘ エラー情報クリア
    Call subClearError
    
    ‘ エラー処理
    If (……) Then
        gobjError.ErrorCode = “ERR001”
        gobjError.ErrorMessage = “エラーメッセージ”
        GoTo ErrorHandler
    End If

    ‘ 途中で正常終了したい場合
    If (……) Then
        GoTo ExitHandler
    End If
    :
    :
ExitHandler:
    Exit Sub

ErrorHandler:
    Call subErrorProcces
    
End Sub

というように書きます。ここで注意すべき点は、
・サブプロシージャを呼び出した後は必ずエラー有無の確認を行う。
・エラー情報は独自のオブジェクトで管理する。
・正常時、異常時両方にラベルを定義し、特に処理がなくても全プロシージャでテンプレートのように使用する。

○エラー情報クラス
‘ エラー情報保持クラス
‘ EXE全体のエラー管理及びエラーリスト処理クラスのデータとして用いる。
‘ 構造体で定義するとコンパイルエラーとなる為、クラスで定義する。

Public ErrorCode            As String   ‘ エラーコード
Public ErrorMessage         As String   ‘ エラーメッセージ
  :
  :


○グローバル定義
‘ 共通エラー情報クラスオブジェクト(アプリケーションで唯一のインスタンス)
Public gobjError                As New ErrorInfo       ‘ 共通エラー情報


○クリア関数
エラー情報インスタンスクリア

Public Sub subClearError()
    ‘ メッセージクリア
    gobjError.ErrorCode = “”
    gobjError.ErrorMessage = “”
End Sub


○エラー処理関数
実行時エラーなどを独自に定義したメッセージに変換する処理などを行う。

Public Sub subErrorProcces()

    ‘ エラー設定処理でOn Error句を用いるとErrオブジェクトがクリアされるので用いない。
    
    ‘ 実行時エラー変換
    Select Case Err.Number
        Case 70
            gobjError.ErrorCode = “ERR002”
        Case XX
            gobjError.ErrorCode = “ERRXXX”
          :
          :
        Case Else
    End Select
    
    If gobjError.ErrorCode <> “” Then Exit Sub
    
    ‘ Runtimeエラーメッセージ取得
    Call subSetRuntimeError

End Sub


こうすることで、VB6であってもエラー処理をビジネスロジックと分離し管理できるようになります。これはAOP(Aspect Oriented Programming)が生まれた目的と同じで、ソフトウェア全体を横断する関心事を分離するものです。

もう一つのエラー処理の方法としては、エラーとしたい場合に「Err.Raise 9999(独自に定義した数値)」などとして実行時エラーを発生させ、最上位の呼び出し元で補足して処理するという方法もあります。

その場合、On Error GoTo~などのエラー処理ルーチンを終了すると、Errオブジェクトがクリアされてしまうので、補足するのはあくまで最上位の呼び出し元のみとする必要があります。
これだと、記述量が少なくシンプルになります。ただErrオブジェクトをクリアしないように気をつける必要があるので、小規模案件や少人数のプロジェクト向きかもしれません。

このように、共通クラス定義や広域変数でのインスタンス保持、プロジェクト分割、チーム内のルールや規約などを組み合わせることで、モダンな言語が備える問題の分割や処理の共通化の仕組みをある程度実現できます。

言うまでもなく、言語仕様に元々分割、共通化の仕組みが備わっている方がコードの統括、見通しの良さ共に良いのであくまでもなんらかの事情によりVB6の採用が最適解である場合のコード整理の仕方ということで参考になればと思います。

エラー処理については以上です。また他のトピックについても書いていきたいと思います。

 

それではまた♪

VB, 社員:プリースト , , ,

VB6によるアーキテクチャ設計 その1 - VB6でオブジェクト指向プログラミングはどこまでできるか?

2014年03月16日 12時44分28秒

こんにちは。プリーストです。

今担当しているお仕事では、開発言語にVB6、DBにOracleを使用しています。
VB6!?と思っていましたが、やってみるとこれが割と綺麗に書けたので思考の流れとアーキテクチャ設計指針をここにまとめておこうかなと思いました。
VB6は1998年リリースとのことですが、その時代としてはかなり先進的で画期的な言語だったのではないでしょうか。Windows95、98といいその頃のMicrosoftはすごかったんですね~。

長くなるのでとりあえず、第一回目では概要と方針を書いてみます。

何か参考になれば嬉しいです。

まずは、求められる非機能要件の整理からです。
・現行システムはVB6で作成したクライアントプログラムとOracleのストアドプロシージャで構成される2階層C/Sシステム
・開発するのは割と小規模のサブシステム
・DB操作はほとんどOracleのストアドプロシージャで行うが、保守時のデバッグが難しいことが課題
・使用するのはイントラネット内部のみで通信資源は潤沢
・将来的に.Net等のオブジェクト指向言語でリプレースする可能性がある

お客様に相談して頂いたのがやはり保守時のデバッグ。これがストアドプロシージャで記述してあるとやりづらいとのこと。
VB6のソースコードも読みづらく、保守効率が悪いというよくある相談をされました。
そして、将来的には.Netでのリプレースも考えているとのこと。
続きを読む…

Oracle, VB, Windows, 社員:プリースト , ,