shimapapa.io

.NET,VB,C#,AzureなどMS関連中心の技術ブログ

【DDD練習】「JR 新幹線 料金ルールを実装してみよう」にチャレンジ(その1)

前置き

『現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法』の著者の増田さんが、
ワークショップで使用する「JR 新幹線 料金ルールを実装してみよう」というサンプルコードをGitHubで公開されておりました。

今回、この課題に実装例を見ないでチャレンジしてみたいと思います。
※19/12/23に公開しましたが、バグがあったので修正して再度公開しました。

実装・前半戦開始

JR新幹線の料金の要件は以下のようになっています。

  • 料金 = 運賃 + 特急料金

  • 特急料金(super express surcharge)

  • 指定席(ひかり)
  • のぞみ割り増し (additional charge)
  • 自由席特急料金 (free seat)
  • 子供料金 (child)

自分はここから、ルールには以下3つの「区分」があると読み取りました。
自由席割引の要件から、「指定・自由」の「席種」、
のぞみ料金は割り増し、という要件から「ひかり・のぞみ」の「特急料金種」、
子供料金は割引き、という要件から「大人・子供」の「料金」区分を抽出しました。

料金

料金区分(FareType)
大人
子供

特急料金

特急料金種(SuperExpressSurchargeType) 席種(SeatType)
ひかり 指定
のぞみ 自由

料金区分(Fare)の実装

  • 料金 = 運賃 + 特急料金

の計算式から 「Fare(料金)」インターフェイス、「運賃(BasicFare)」クラス、「特急料金(SuperExpressSurcharge)」インターフェイスを実装しました。
「Fare」を実装した「大人料金(AdultFare)」「子供料金(ChildFare)」の具象クラスを実装しました。 「子供料金(ChildFare)」は、「運賃」と「特急料金」を割引するロジックが実装されています。

public class BasicFare
    {
        public readonly int value;

        public BasicFare(int value)
        {
            this.value = value;
        }

    }
public interface SuperExpressSurcharge
    {
        public int value { get; }
    }
public interface Fare
    {
        public BasicFare basicFare { get; }
        public SuperExpressSurcharge superExpressSurcharge { get; }

        int value();
    }
public class AdultFare : Fare
    {
        public BasicFare basicFare { get; }

        public SuperExpressSurcharge superExpressSurcharge { get; }

        public AdultFare(BasicFare basicFare, SuperExpressSurcharge superExpressSurcharge)
        {
            this.basicFare = basicFare;
            this.superExpressSurcharge = superExpressSurcharge;
        }

        public int value()
        {
            return  basicFare.value + superExpressSurcharge.value;
        }
    }
public class ChildFare : Fare
    {
        public BasicFare basicFare { get; }

        public SuperExpressSurcharge superExpressSurcharge { get; }

        private const double discountRate = 0.5;
        private const double roundDownNumber = 0.1;

        public ChildFare(BasicFare basicFare, SuperExpressSurcharge superExpressSurcharge)
        {
            this.basicFare = basicFare;
            this.superExpressSurcharge = superExpressSurcharge;
        }
        public int value()
        {
            return calculateChildFare(basicFare.value) + calculateChildFare(superExpressSurcharge.value);
        }

        public int calculateChildFare(int value)
        {
            return (int)Math.Floor((value * discountRate) * roundDownNumber) * 10;
        }

    }

席種(SeatType)の実装

続いて「席種(SeatType)」の実装です。
「席種(SeatType)」インターフェイスから「指定席(ReservedSeatCharge)」クラスと、「自由席(FreeSeatCharge)」クラスを実装。
「自由席」の料金は「ひかり」の料金から自由席割引額を引いた額になるので、 「自由席割引(FreeSeatDiscount)」クラスを実装。
「自由席(FreeSeat)」クラスは、「ひかり料金(HikariCharge)」クラスと「自由席割引(FreeSeatDiscount)」クラスを内包したクラスとして表しました。
「指定席」は「ひかり」の料金そのままなので、「ひかり料金(HikariCharge)」を内包しただけのクラスとなります。

class HikariCharge
    {
        public readonly int value;

        public HikariCharge(int value)
        {
            this.value = value;
        }
    }
class FreeSeatDiscount
    {
        public int value;

        public FreeSeatDiscount(int value)
        {
            this.value = value;
        }

    }
public interface SeatCharge
    {
        public int value { get; }
    }
public class ReservedSeatCharge :SeatCharge
    {
        private readonly HikariCharge hikariCharge;

        public ReservedSeatCharge(HikariCharge hikariCharge)
        {
            this.hikariCharge = hikariCharge;
        }

        public int value => hikariCharge.value;

    }
public class FreeSeatCharge : SeatCharge
    {
        private readonly HikariCharge hikariCharge;
        private readonly FreeSeatDiscount freeSeatDiscount;
        public FreeSeatCharge(HikariCharge hikariCharge, FreeSeatDiscount freeSeatDiscount)
        {
            this.hikariCharge = hikariCharge;
            this.freeSeatDiscount = freeSeatDiscount;
        }

        public int value => this.hikariCharge.value - freeSeatDiscount.value;

    }

特急料金種(SuperExpressSurchargeType)の実装

続いて、特急料金を表すSuperExpressSurchargeインターフェイスから、「ひかり(Hikari)」クラス、「のぞみ(Nozomi)」クラスを実装しました。
それぞれ、「席種(SeatCharge)」を内包させています。
「席種(SeatCharge)」が「ひかりの料金(HikariCharge)」を内包しているためです。
「のぞみ」の料金は、以下の要件から「ひかり」の「料金」に割増するモノなので、「のぞみ(Nozomi)」は「席種(SeatCharge)」クラスと、「のぞみ割増料金(NozomiAddtionalCharge)」クラスを内包したクラスとしました。

のぞみ割り増し (additional charge)

ひかりの特急料金に、以下の金額を加算する。

  • 新大阪まで 320 円
  • 姫路まで 530 円
public class Hikari : SuperExpressSurcharge
    {
        public int value => seatCharge.value;

        private readonly SeatCharge seatCharge;
        public Hikari(SeatCharge seat)
        {
            this.seatCharge = seat;
        }

    }
public class NozomiAdditionalCharge
    {
        public readonly int value;

        public NozomiAdditionalCharge(int value)
        {
            this.value = value;
        }
    }
public class Nozomi : SuperExpressSurcharge
    {
        private readonly SeatCharge seatCharge;
        private readonly NozomiAdditionalCharge nozomiAdditionalCharge;
        public Nozomi(SeatCharge seatCharge, NozomiAdditionalCharge nozomiAdditionalCharge)
        {
            this.seatCharge = seatCharge;
            this.nozomiAdditionalCharge = nozomiAdditionalCharge;
        }

        public int value => seatCharge.value + nozomiAdditionalCharge.value;

    }

「区分オブジェクト」を使いさらに分かりやすく

上記の例では計算方法ごとに関数を別にしていますが、実際にUIから「大人 or 子供」「のぞみ or ひかり」「指定席 or 自由席」といった条件から各クラスのインスタンスを作成するとなった場合、
ここまでの実装だけだとIf文を使って分岐することになってしまいそうです。
そこで各クラスの生成を「区分オブジェクト」にやらせることでコードをもっと分かりやすくしたいと思います。

public class SuperExpressSurchargeType
    {
        private readonly SeatCharge seatCharge;
        private readonly NozomiAdditionalCharge nozomiAdditionalCharge;

        public SuperExpressSurchargeType(SeatCharge seatCharge, NozomiAdditionalCharge nozomiAdditionalCharge)
        {
            this.seatCharge = seatCharge;
            this.nozomiAdditionalCharge = nozomiAdditionalCharge;
        }

        public  Hikari Hikari()
        {
            return new Hikari(seatCharge);
        }

        public  Nozomi Nozomi()
        {
            return new Nozomi(seatCharge, nozomiAdditionalCharge);
        }

        public SuperExpressSurcharge valueOf(string name)
        {
            var method = typeof(SuperExpressSurchargeType).GetMethod(name);
            return method.Invoke(this, null) as SuperExpressSurcharge;
        }

    }
class SeatType
    {
        private readonly HikariCharge hikariCharge;
        private readonly FreeSeatDiscount freeSeatDiscount;

        public SeatType(HikariCharge hikariCharge, FreeSeatDiscount freeSeatDiscount)
        {
            this.hikariCharge = hikariCharge;
            this.freeSeatDiscount = freeSeatDiscount;
        }

        public ReservedSeat Reserved()
        {
            return new ReservedSeat(hikariCharge);
        }

        public FreeSeat Free()
        {
            return new FreeSeat(new ReservedSeat(hikariCharge), freeSeatDiscount);
        }

        public Seat valueOf(string name)
        {
            var method = typeof(SeatType).GetMethod(name);
            return method.Invoke(this, null) as Seat;
        }

    }
public class FareType
    {
        private readonly BasicFare basicFare;
        private readonly SuperExpressSurcharge superExpressSurcharge;

        public FareType(BasicFare basicFare, SuperExpressSurcharge superExpressSurcharge)
        {
            this.basicFare = basicFare;
            this.superExpressSurcharge = superExpressSurcharge;
        }

        public AdultFare Adult()
        {
            return new AdultFare(basicFare, superExpressSurcharge);
        }

        public ChildFare Child()
        {
            return new ChildFare(basicFare, superExpressSurcharge);
        }

        public Fare valueOf(string name)
        {
            var method = typeof(FareType).GetMethod(name);
            return method.Invoke(this, null) as Fare;
        }

    }

ポイントはvalueOfメソッドです。
各区分オブジェクトクラスに、インスタンスを生成する関数を実装。
.GetMethodにより文字列で対象のクラスを指定し、インスタンスを生成している箇所です。
上記の手法を用いることで、IF文を使って分岐する必要がなくなります。
FareSuperExpressSurchargeSeatTypeを抽象化した実装もここで効果が出ていると感じます。

区分オブジェクト導入後の使用例

料金を計算するサービスクラス「FareSystemService」です。
各料金は出発地・目的地ごとに異なるため、これらはDBなどで定義されていると仮定し、「FaresRepository」というリポジトリークラスを実装。
出発地(Departure)、目的地(Destination)ごとの運賃、特急料金等を保持した「FareByrRoute」オブジェクトを、「FaresRepository」から取得する形にしました。
※自由席割引額(FreeSeatDiscount)もマスターテーブルのような場所で定義するのがよさそうですが、例では省略してハードコーディングしてしまっています。

public class FareSystemService
    {
        private readonly IFaresRepository _faresRepository;

        public FareSystemService(IFaresRepository faresRepository)
        {
           _faresRepository = faresRepository;
        }

        public int calculateFare(Departure departure, 
                                              Destination destination,  
                                              string superExpressName, 
                                              string seatName, 
                                              string fareName)
        {
            FareByRoute fareByRoute = _faresRepository.GetFareByRoute(departure, destination);

            SeatChargeType seatType = new SeatChargeType(fareByRoute.hikariCharge, new FreeSeatDiscount(530));
            SeatCharge seat = seatType.valueOf(seatName);

            SuperExpressSurchargeType superExpressType = new SuperExpressSurchargeType(seat, fareByRoute.nozomiAdditionalCharge);
            SuperExpressSurcharge superExpressSurcharge = superExpressType.valueOf(superExpressName);

            FareType fareType = new FareType(fareByRoute.basicFare, superExpressSurcharge);
            Fare fare = fareType.valueOf(fareName);

            return fare.value();
        }
public interface IFaresRepository
    {
        public FareByRoute GetFareByRoute(Departure departure, Destination destination);
    }
public class Departure
    {
        public readonly string value;

        public Departure(string value)
        {
            this.value = value;
        }
    }
public class Destination
    {
        public readonly string value;

        public Destination(string value)
        {
            this.value = value;
        }

    }
public class FareByRoute
    {
        public readonly Departure departure;
        public readonly Destination destination;
        public readonly BasicFare basicFare;
        public readonly HikariCharge hikariCharge;
        public readonly NozomiAdditionalCharge nozomiAdditionalCharge;

        public FareByRoute(Departure departure,
                                        Destination destination,
                                        BasicFare basicFare,
                                        HikariCharge hikariCharge,
                                        NozomiAdditionalCharge nozomiAdditionalCharge)

        {
            this.departure = departure;
            this.destination = destination;
            this.basicFare = basicFare;
            this.hikariCharge = hikariCharge;
            this.nozomiAdditionalCharge = nozomiAdditionalCharge;
        }

    }

まとめ

前半戦の実装は以上です。 残りは後日「その2」で公開できればと思います。
残る要件は

  • 割引 (discount)
  • 季節(season)による特急指定席料金の変動

です。 今回の練習の結果は以下GitHubリポジトリに公開しています。

github.com

追記:続きは以下

rikupapa-shima.hatenablog.com

2019年の振り返り

今日で仕事納めでした。
今年、取り組んできたことを振り返って来年の行動に繋げていきたいと思います。

新しく始めたこと

このブログ

アウトプットの一環として、技術系の情報と読書感想をまとめた本ブログをはじめました。
本記事を入れて29投稿できたので、だいたい月2ぐらいのペースで投稿し続けることができました。
スターを頂いたり、Qiita上の記事からリンクを貼っていただいたり、リアクションをもらえるようになってきたのがとても嬉しかったです。
来年も引き続きアップし続けられるように頑張りたいと思います。
以下、リンクしていただいた元記事。

qiita.com

qiita.com

社内勉強会

社内で有志を募って社内勉強会「ハンガーフライト」を開催し、現場で実践してきたことなどを発表しました。
自分含めて参加者は5名だけだったので少し寂しかったのですが、入社直後の方が即参加を表明してくださったり、来なかった方からも「あれ、どうだった?次は参加したいな」とリアクションをいただけたりしたので、 まずは継続的に開催していければと考えています。
「ハンガーフライト」については書籍「カイゼン・ジャーニー」からインスパイアを受けました。

www.asakara.net

資格取得(AZ-900)

初めてベンダー系の資格取得に挑戦しました。

rikupapa-shima.hatenablog.com

まだ現場で活かす機会はないのですが、引き続きAzureの知識はインプットしていけたらと思います。
来年は上位試験の「AZ-203」にチャレンジしたいですが、最近全然勉強時間を取れていないので、その辺りが課題です。

Udemy受講

有料のオンライン学習サイトも、今年はじめて体験しました。
DockerとJenkins、AZ-900の試験対策についてのレッスンを受講しました。
1つの動画が長くても15分程度なので、コツコツ勉強するのにも向いているなと思いました。
また利用してみたいです。

rikupapa-shima.hatenablog.com

rikupapa-shima.hatenablog.com

読書

今年読んだ本は以下の記事でまとめました。

rikupapa-shima.hatenablog.com

まとめ

2018年の10月に転職して今の会社に入社。
自社パッケージソフトの開発エンジニアからSESのエンジニアに転身。
2019年1月に参画した現場で引き続き働いています。
新しい環境、新しいシステムに触れることで自分の得意なこと、課題や今後やりたいことがまた見えてきたように思えます。
来年もより成長できるよう、引き続きインプット・アウトプットを続けていきたいと思います。

2019年の読書の振り返り

2018年の振り返り

rikupapa-shima.hatenablog.com

2019年に読んだ本

rikupapa-shima.hatenablog.com

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ

  • 作者:ミック
  • 出版社/メーカー: 翔泳社
  • 発売日: 2012/03/16
  • メディア: 単行本(ソフトカバー)

rikupapa-shima.hatenablog.com

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

SQL実践入門──高速でわかりやすいクエリの書き方 (WEB+DB PRESS plus)

  • 作者:ミック
  • 出版社/メーカー: 技術評論社
  • 発売日: 2015/04/11
  • メディア: 単行本(ソフトカバー)

rikupapa-shima.hatenablog.com

rikupapa-shima.hatenablog.com

rikupapa-shima.hatenablog.com

rikupapa-shima.hatenablog.com

rikupapa-shima.hatenablog.com

rikupapa-shima.hatenablog.com

以下読み途中

Effective C# 6.0/7.0

Effective C# 6.0/7.0

総括

昨年に比較して、今年はがっつり技術系の書籍を選んできました。 特に * 「Adaptive Code」 * 「.NETのエンタープライズアプリケーションアーキテクチャパターン」 * 「現場で役立つシステム設計の原則」 辺りはDDD(ドメイン駆動設計)とそれらに関連する知識を得ることができたと実感。 来年も引き続きインプットしていきたいです。

VB.NET + Windows Forms + Unity.Container で Dependency Injection(依存性注入)

前置き

この記事はQiita Visual Basic Advent Calendar 2019 の11日目の記事です。

qiita.com

.NET Core 3.0 では WinForms が対応され、Visual Basic 16.0 (Visual Studio 2019) もリリースされました。 Visual Studio 2019 16.4 では、まだ .NET Core 3.1 + WinForms + Visual Basic のテンプレートは対応されていないようでしたが、
.NET Core 3系におけるVBの対応は前向きに進んでいるように見えるので、今後の展開に注目しています。

developercommunity.visualstudio.com

devblogs.microsoft.com

で(唐突)、Advent Calendar 用のネタが何か無いか探したところ、
「Adaptive Code」で読んだ Unitiy を利用した Dependency Injection を VB + WinForms で行えるか試してみることにしました。

rikupapa-shima.hatenablog.com

はじめに

まず、以下のコードはForm1の中でrepositoryインスタンスをNewしているので、
Form1はrepositoryに依存している関係にあります。

Public Class Form1

    Private ReadOnly repository As New Repository

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MessageBox.Show(Join(repository.getAll.ToArray, ","))
    End Sub

End Class

Unitiy.Container を用いた依存性注入

ここから依存性注入の形に変えていくには、RepositoryIRepositoryインターフェースで抽象化し、 コンストラクタの引数でインスタンスを渡す形に変更します。

Public Class Form1

    Private ReadOnly repository As IRepository

    Sub New(repository As IRepository)

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        Me.repository = repository
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        MessageBox.Show(Join(repository.getAll.ToArray, ","))
    End Sub
End Class
Public Interface IRepository

    Function getAll() As List(Of String)

End Interface
Public Class Repository
    Implements IRepository

    Public Function getAll() As List(Of String) Implements IRepository.getAll
        Return New List(Of String) From {"1", "2", "3"}
    End Function
End Class

NuGet でUnity.Containerをインストールします。

f:id:rikupapa-shima:20191209233730p:plain

メインエントリポイントModule Programを手動で追加して、
そこにコンテナの初期化とインターフェイスインスタンス生成の紐付けを記述します。
名前空間UnityのImportを忘れずに。

Imports Unity

Public Module Program

    <STAThread()>
    Sub Main()

        Dim container As IUnityContainer = New UnityContainer

        container.RegisterInstance(Of IRepository)(New Repository())

        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(container.Resolve(Of Form1))
    End Sub
End Module

ポイントはcontainer.Resolve(Of Form1)により、
Form1のコンストラクSub New(repository As IRepository)の引数repositoryに NewされたRepositoryインスタンスが渡されることです。 依存性をクラスの外側から注入できたことになります。

Unity.Configuration を使って、App.Config でインターフェイスインスタンスを紐付け

ここからさらに、Unity.Configuration を使って、
App.Config でインターフェイスインスタンスの紐付けを記述する形に変更します。 NuGetからUnity.Configurationをインストールします。

f:id:rikupapa-shima:20191209235045p:plain

インストール後、Programモジュールで名前空間Microsoft.Practices.Unity.Configurationをインポートします。
設定ファイルの値をContainerに読み込む拡張メソッドLoadConfigurationMicrosoft.Practices.Unity.Configurationに存在するためです。

Imports Unity
Imports Microsoft.Practices.Unity.Configuration

Public Module Program

    <STAThread()>
    Sub Main()

        Dim container As IUnityContainer = New UnityContainer
        container.LoadConfiguration()

        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(container.Resolve(Of Form1))
    End Sub
End Module

App.Config には以下を追加します。

<configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration" />
</configSections>
<startup>
   <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
 <assembly name="WindowsAppVb" />
 <namespace name="WindowsAppVb" />
<container>
  <register type="IRepository" mapTo="Repository" />
</container>
</unity>

assembly nameも記述しないと実行時に
「The type name or alias ”xxxxx“ could not be resolved. Please check your configuration file and verify this type name.”」
が発生しました。

以上、「VB.NET + Windows Forms + Unity で Dependency Injection(依存性注入)」でした。

おわりに

VB.NETでWinForm、ASP.NET WebFormの保守開発をしているVBerの皆様、
いやいや、ていうかVB6.0の保守開発しているんですけど、というVBerの皆様、 良いお年を。来年も頑張りましょう。

【読了】「現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法」(のCHAPTER1)

前置き

今回はこちらの書籍のCHAPTER1についての感想文です。

現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法

現場で役立つシステム設計の原則 ~変更を楽で安全にするオブジェクト指向の実践技法

以下、本書籍の「はじめに」からどんな書籍かのさわりを。

本書では 、私が業務アプリケ ーションの変更に苦しんだ経験をもとに 、オブジェクト指向設計のやり方と考え方 、効果があった解決策を 、具体的なソースコ ードを示しながら紹介していきます 。特に「なぜそうするのか 」を重視して説明します 。

ドメイン駆動設計の考え方が基本とされ、UI、データベースやAPIなどの設計も交えた、オブジェクト指向設計のプラクティスを学ぶことができます。

CHAPTER1要約

ドメインオブジェクト

第1章では、「なぜソフトウェアの変更は大変なのか」という問いかけから始まります。

では、その原因になってしまう要因とは…

そしてその原因とは設計に問題があるからです。

変更が大変なプログラムは以下のような特徴(問題点)があります。

変更が大変なプログラムの特徴は次の 3つです 。

・メソッドが長い

・クラスが大きい

・引数が多い

プログラムを読みやすくし、変更をしやすくするに以下のような方法があります。

・名前は (略語ではなく )普通の単語を使う

・数行のコ ードを意味のある単位として 「段落 」に分ける

・ 「目的別の変数 」を使う ( 1つの変数を使いまわさない )

・意味のあるコ ードのまとまり (段落 )を 「メソッド 」として独立させる

・業務の関心事に対応したクラス (ドメインオブジェクト )を作る

上記の上から4つの方法を順に行なっていくと、意味的に重複のあるメソッドは1つの関心ごとに対応したクラスに集約されていき、5つ目のドメインオブジェクトが作られていくのだと思います。

ドメインオブジェクト」とは

「送料 」クラスのように 、業務で使われる用語に合わせて 、その用語の関心事に対応するクラスをドメインオブジェクトと呼びます 。

アプリケ ーションの対象領域 (ドメイン )の関心事を記述したオブジェクトという意味です 。

ドメインオブジェクト」を作ることこそ、オブジェクト指向らしい設定のアプローチです。

このように業務の用語と 、直接対応するドメインオブジェクトを用意することが 、業務アプリケ ーションの変更を容易にするオブジェクト指向らしい設計のアプロ ーチです 。また 、業務を理解するために要求を分析し 、そこで発見した業務の関心事の単位を 、そのままプログラミング単位としてクラスで表現するのが 、オブジェクト指向開発のやり方です 。

値オブジェクト

クラスはデータとロジックの固まりです。

ロジックはデータを使った演算(加工/判断/計算)です。

業務アプリケーションにおける基本データ型は

  • 数値
  • 日付
  • 文字

です。

ところが「数量」や「金額」をプログラム言語における数値型=intやBig Decimalでそのまま定義してしまうと、小数点以下や非常に大きな数値など、業務的に不正な値も扱えることになってしまう落とし穴があります。

そこで正しい数量を扱うためのQuantityという独自クラスを定義します。

このような独自クラスを値オブジェクト(Value Object)と呼びます。

値の種類ごとに専用の型を用意するとコ ードが安定し 、コ ードの意図が明確になります 。このように 、値を扱うための専用クラスを作るやり方を値オブジェクト ( V a l u e O b j e c t )と呼びます 。

値オブジェクトは「不変」にする、というプラクティスがあります。

不変にすると以下のようなメリットがあります。

値ごとに別のオブジェクトを用意することで 、一つひとつのオブジェクトの用途が限定され 、プログラムが安定します 。プログラムの途中で内部の値が変化するときに起きがちな副作用を防ぐことができます 。

値オブジェクトを不変にするやり方を「完全コントラクタ」と呼びます。

このような設計のやり方を完全コンストラクタと呼びます 。オブジェクトの生成時に 、オブジェクトの状態を完全に設定してしまうやり方です 。

※本書籍では語られておりませんが、値オブジェクトは「全プロパティ値が同じ値なら、インスタンスが異なっても同じオブジェクト」という特徴も持っています。

以下、参考サイト。

www.infoq.com

値オブジェクトにより独自の型を定義することにより、引数の渡し間違えも防げるようになります。

例えば「金額」と「数量」をそれぞれint型の引数で受け取る関数は、それらを逆に渡されてもコンパイルエラーにはなりません。 つまり、プログラム的には正解でも業務ロジック的には間違い、という状態になりえてしまいます。 そこで引数の型をMoney型、Quantity型といった独自の型にすれば、逆に渡そうとしてしまうコードを書いても、コンパイルエラーで検知できるようになります。

コレクションオブジェクト/ファーストクラスコレクション

オブジェクトを複数持つ配列やコレクションの演算コードは複雑になりがちですが、値オブジェクトと同じく専用の小さなクラスにまとめることでプログラムがわかりやすくなります。

考え方は値オブジェクトと同じです 。デ ータと関連するロジックは 、 1つのクラスに集めます 。 i n t型の変数を 1つ持った 「数量 」の専用クラスを独自に作ったように 、コレクション L i s t < C u s t o m e r >型の変数を 1つだけ持った 「顧客一覧 」の専用クラスを独自に宣言します 。

このようなクラスをコレクションオブジェクトまたはファーストクラスコレクションと呼びます。

このように 、コレクション型のデ ータとロジックを特別扱いにして 、コレクションを 1つだけ持つ専用クラスを作るやり方をコレクションオブジェクトあるいはファ ーストクラスコレクションと呼びます 。

コレクションを操作するロジックをコレクションオブジェクトに閉じ込めると 、コレクションオブジェクトを使う側のコ ードが単純になります 。

素数のチェックやル ープ処理は 、すべてコレクションオブジェクトがやってくれます 。使う側はコレクションオブジェクトのメソッドを呼ぶだけです 。

値オブジェクトと同様に、コレクションオブジェクトも出来るだけ「不変」に設計することでプログラムが安定します。

コレクションの操作を安定させる方法は 、 3つあります 。

・コレクション操作のロジックをコレクションオブジェクトに移動する

・コレクション操作の結果も同じ型のコレクションオブジェクトとして返す

・コレクションを 「不変 」にして外部に渡す

※.NETにおいてコレクションオブジェクトを作る場合、Collectionクラスを使うのが良さそうです。 以下.NETの公式リファレンスより。

docs.microsoft.com

まとめ

以上、本書籍のチャプター1では

  • ドメインオブジェクト
  • 値(バリュー)オブジェクト
  • コレクションオブジェクト/ファーストクラスコレクション

といったオブジェクト指向設計/DDDのプラクティスが学べます。

この3つだけでも知っている/知らないでクラス設計にかなり差が出てくるのはと感じました。

もっと経験年数が若いうちに、これらのプラクティスをキャッチできていたら…

CHAPTER2以降の感想はまたの機会に。

コラム

あの時、値オブジェクトを導入していれば…!と過去の案件の事例を思い出しました。

前職では介護サービス業向けのパッケージソフトを開発していました。

介護サービスは国で規定された種類が定義されており、コード化されております。

介護保険サービス種類コード一覧  

2ページ目右側を見ると、平成27年4月より背景黄色で新しいサービス種類コードが追加されています。
介護保険法の改正によりサービス種類が追加され、それまで数値のみだったコード体系にA1とアルファベットが混在するコードが追加されました。
開発していたシステムではサービス種類コードをint型で定義してしまっていたので、この法改正によりstring型で扱う変更を入れる羽目になってしまいました…。
サービス種類コードはシステムの至る所でint型で取り扱われていたので、修正作業にかなりの工数を取られてしまいました。
このような業務レベルの変更に対しても、サービス種類コードを「ServiceTypeCode」といった値オブジェクトで定義しておけば、被害は少なかったでしょう。
詳細の説明は省きますが、サービス種類コードと「サービス項目コード」を組み合わせた「サービスコード」という概念もあります。 これらも踏まえてコード化すると以下のような設計が考えられます。

public class ServiceTypeCode
    {
        public string value { get; private set; }

        public ServiceTypeCode(string value)
        {
            if (value.Length != 2)
            {
                throw new ArgumentException("サービス種類コードは2桁です");
            }

            this.value = value;
        }
    }

    public class ServiceItemCode
    {
        public string value { get; private set; }

        public ServiceItemCode(string value)
        {
            if (value.Length != 4)
            {
                throw new ArgumentException("サービス項目コードは4桁です");
            }

            this.value = value;
        }
    }

    public class ServiceCode
    {
        public ServiceTypeCode serviceTypeCode { get; private set; }
        public ServiceItemCode serviceItemCode { get; private set; }

        public ServiceCode(ServiceTypeCode serviceTypeCode, ServiceItemCode serviceItemCode)
        {
            this.serviceTypeCode = serviceTypeCode;
            this.serviceItemCode = serviceItemCode;
        }

        public string GetValue()
        {
            return this.serviceTypeCode.value + this.serviceItemCode.value;
        }

    }

【読了】「他者と働く 「わかりあえなさ」から始める組織論』

今回はこちらの書籍の感想文。

他者と働く──「わかりあえなさ」から始める組織論 (NewsPicksパブリッシング)

他者と働く──「わかりあえなさ」から始める組織論 (NewsPicksパブリッシング)

 

 

要約

私たちの社会はさまざまな問題を抱えていて、そのほとんどは「適応課題」といえる。

ハ ーバ ード ・ケネディ ・スク ールで 2 5年間リ ーダ ーシップ論の教鞭をとり 、 「最も影響を受けた授業 」に選ばれ続け 、 I B M 、マイクロソフト 、マッキンゼ ー 、世界銀行などのアドバイザ ーも務めるロナルド ・ハイフェッツ 。彼は 、既存の方法で解決できる問題のことを 「技術的問題 」 ( t e c h n i c a l p r o b l e m ) 、既存の方法で一方的に解決ができない複雑で困難な問題のことを 「適応課題 」 ( a d a p t i v e c h a l l e n g e )と定義しました 。

 

本書では適応課題を解く鍵、「対話」について語られる。

見えない問題 、向き合うのが難しい問題 、技術で一方的に解決ができない問題である 「適応課題 」をいかに解くか──それが 、本書でお伝えする 「対話 」です 。

「対話」とはおしゃべりではなく、新しい関係性を構築すること。

対話とは 、一言で言うと 「新しい関係性を構築すること 」です 。これは哲学者のマルティン ・ブ ーバ ーやミハイル ・バフチンらが用いた 「対話主義 」や 「対話概念 」と呼ばれるものに根ざしています 。

関係性には大きく2つの分類がある。

哲学者のマルティン ・ブ ーバ ーは 、人間同士の関係性を大きく 2つに分類しました 。ひとつは 「私とそれ 」の関係性であり 、もうひとつは 「私とあなた 」の関係性です 。

そして、適応課題には4タイプがある。

1つ目の 「ギャップ型 」は 、大切にしている 「価値観 」と実際の 「行動 」にギャップが生じるケ ースです 。

2つ目の 「対立型 」は 、互いの 「コミットメント 」が対立するケ ースです 。

3つ目の 「抑圧型 」は 、 「言いにくいことを言わない 」ケ ースです 。 

4つ目の 「回避型 」は 、痛みや恐れを伴う本質的な問題を回避するために 、逃げたり別の行動にすり替えたりするケ ースです 。

これらのような適応課題が見出された時、

私たちは「私とそれ」の関係性を改める必要がある。

そこで変える必要があるのは「ナラティブ」。

その一歩目として 、相手を変えるのではなく 、こちら側が少し変わる必要があります 。そうでないと 、そもそも背後にある問題に気がつけず 、新しい関係性を構築できないからです 。しかし 、 「こちら側 」の何が変わる必要があるのでしょうか 。それはナラティヴです 。 「ナラティヴ ( n a r r a t i v e ) 」とは物語 、つまりその語りを生み出す 「解釈の枠組み 」のことです 。

ナラティヴは 、私たちがビジネスをする上では 、 「専門性 」や 「職業倫理 」 、 「組織文化 」などに基づいた解釈が典型的かもしれません 。

例で言うと「医者と患者」、「上司と部下」の関係性にはそれぞれのナラティブがある。

(IT系の仕事だと「受注者と発注者」「開発と営業」とか?)

どちらのナラティブが正しいか、正しくないかということではない。

ナラティブの間の溝を見つけて「橋をかけていく」のが「対話」。

こちら側のナラティヴに立って相手を見ていると 、相手が間違って見えることがあると思います 。しかし 、相手のナラティヴからすれば 、こちらが間違って見えている 、ということもありえるのです 。こちらのナラティヴとあちらのナラティヴに溝があることを見つけて 、言わば 「溝に橋を架けていくこと 」が対話なのです 。

「溝に橋をかける」には4つのプロセスがある。

1 .準備 「溝に気づく 」相手と自分のナラティヴに溝 (適応課題 )があることに気づく

2 .観察 「溝の向こうを眺める 」相手の言動や状況を見聞きし 、溝の位置や相手のナラティヴを探る

3 .解釈 「溝を渡り橋を設計する 」溝を飛び越えて 、橋が架けられそうな場所や架け方を探る

4 .介入 「溝に橋を架ける 」実際に行動することで 、橋 (新しい関係性 )を築く

準備の段階ではまずナラティブを脇に置いてみる。

脇に置いてみることは 、同時に 、相手のナラティヴと自分のナラティヴの間に溝があることを認めるということを意味してもいます 。つまり 、自分のナラティヴを今までは疑うことなく生きてきたけれど 、それとは違うナラティヴがあるかもしれない 、という可能性をここでは一度受け入れ 、相手にも相手なりに何か事情があるのかな 、見えている景色が違うのかな 、と想像してみることです 。

対話のプロセスは一対一の関係に思えるが、実は関係性に働きかける行為である。

これは哲学者オルテガ ・イ ・ガセットが示した考えで 、個人とは 「個人と個人の環境 」によって作られているということに気付くと 、理解がしやすいでしょう 。つまり 、 「私 」や 「あなた (他者 ) 」とは 、果たして一対一の人間なのか 、ということです 。

つまり 、人はその人の置かれた人間関係や環境にそもそも埋め込まれた作られた存在なのです 。

したがって 、相手をよく観察することは 、相手の埋め込まれている関係性を理解するということを意味します 。

組織における対立構造のひとつとして、「新規事業部」と「既存事業部」の対立といったものがある。

この対立では「総論賛成、各論反対」のような状況になってしまう。

もちろん 、そうした既存事業部の人たちも 、決して会社として新規事業開発をしていくことに反対ではないのです 。しかし 、自分たちの現場は 、厳しい状況に置かれてもいる 。だから 、総論賛成 、各論反対のような状況になってしまいます 。

こうした状況では、以下のようなプロセスが二巡目の対話の鍵となる。

1 .準備 :相手を問題のある存在ではなく 、別のナラティヴの中で意味のある存在として認める

2 .観察 :関わる相手の背後にある課題が何かをよく知る

3 .解釈 :相手にとって意味のある取り組みは何かを考える

4 .介入 :相手の見えていない問題に取り組み 、かゆいところに手が届く存在になる

そして、対立構造の中では自身のナラティブの偏りに向き合うこと。

中立な人間は原理的に考えてもこの世界には存在しません 。誰もがそれぞれのナラティヴを生きているという意味で偏った存在であり 、それは自分もそうだということです 。そうであるならば 、まず自らの偏りを認めなければ 、他者の偏りを受け入れるのは難しいでしょう 。他者の偏りとは 、ナラティヴの隔たりのことです 。それは時に 、自分はよいことをやっていると思っていたが 、もしかしたら 、会社の未来には何も貢献していなかったのかもしれない 、ということに気づくのかもしれません 。

上司と部下の関係では、両者とも以下のようなナラティブに偏ってしまうことがある。

立場の弱い側には 、ひとつ大きな罠があります 。立場が上の人間を悪者にしておきやすい 「弱い立場ゆえの正義のナラティヴ 」に陥っている 、ということです 。立場の弱い側は 、いくらでも人のせいにして 、逃げ道があります 。多くの場合 、まだ若いですから 、うまくいかなくても再起するチャンスがあります 。

マネジメントをする際に気をつけたいのは 、上の立場の人間 、とりわけ経営陣は強力な権力を持っているため 、現場の人たちがいつも素直に自分に話をしてくれることはない 、ということです 。

権力を自覚せずに観察を試みることが観察を失敗させます 。

自らの権力によって 、見たいものが見られない 、という不都合な現実を見ることこそが対話をする上では不可欠なのです 。

そんな中では、上司は部下が仕事のナラティブにおいて主人公になれるように働きかけてあげるようにする。

人が育つというのは 、その人が携わる仕事において主人公になることだと考えます 。

この主人公 、ないし当事者としての側面がうまく構築されていかないと 、いつも頑張っているのに認めてもらえない (他者視点での自分の評価に依拠している ) 、仕事の意味を感じられない (生活のためにつまらない仕事を我慢している ) 、自分が生かされていない (自分のために組織があるという過度な自己意識 )という状態から抜け出せないまま 、悶々として過ごすことになります 。

上司の視点と尺度で 「部下の能力を向上させよう 」というナラティヴを一度脇に置くことが大切なのではないでしょうか 。一度脇に置いた上で 、対話のプロセスを大切にしながら 、部下が仕事のナラティヴにおいて主人公になれるように助けるのが上司の役割なのではないでしょうか 。

対話の中では以下のような罠に陥ることがある。

①気づくと迎合になっている

②相手への押しつけになっている

③相手と馴れ合いになる

④他の集団から孤立する

⑤結果が出ずに徒労感に支配される

迎合にならず、対話に挑むということは「誇り高く生きること」。

対話に挑むことを別な言い方をするならば 、それは組織の中で 「誇り高く生きること 」です 。

私たちは 、何者なのでしょうか 。何のために頑張っているのでしょうか 。そのことを見定めることによって 、私たちは 、困難の前にあって 、常に挫かれ 、改められることが必然である暫定的な理想を掲げ続け 、歩むことができるはずです 。相手を観察し 、対岸に橋を架けることは単なるその一過程に過ぎません 。そのことは目的ではないのです 。誇り高く生きることは 、孤独であることを避けられません 。しかし 、その孤独ゆえに 、他者に迎合するのではなく 、孤独にこそ私たちの理想が刻まれていることを思い返すとよいでしょう 。

そして、信頼できる仲間と取り組むこと。

可能であれば信頼できる仲間とそのことに取り組むのもよいアイデアです 。孤独を大切にするためには 、孤立してはならないからです 。そのためには 、信頼できる仲間が現れるのを待つのではなく 、あなたが他者に信頼されるように働きかけることが大切です 。信頼があって私たちが行動できるのではなく 、私たちの行動があって信頼がそこに芽生えるのだということを忘れないでください 。

「馴れ合い」は抑圧型の適応課題になっていくこととある。

つまり 、この関係性を維持すべく 、言いたいことが言えない 「抑圧型 」の適応課題が生じることを意味します 。これは 、ある意味で 、とてもよい関係が築けたことの裏返しでもあるために 、難しい問題でもあります 。

一度出来上がったものを変えていくには大きな痛みが伴うが、行動を起こさなれければ何も変えられない。

しかしはたと立ち止まると違和感が残ることはあるはずです 。従って 、何かおかしい 、と思う違和感を表出することを恐れないでください 。馴れ合いの結果排除される人が出ていたり 、向き合うべき問題に向き合えていないことに気がついたならば 、それを変えていくための行動を起こさなければ 、何も変わりません 。

一度出来上がったものを変えていくことは 、そうしたものが出来上がるよりも前から作っていくよりも大きな痛みが伴います 。しかし 、その痛みに意味を見出すことができれば 、乗り越えようとする勇気が湧いてくるはずです 。その意味を生み出すもの 、それは 、私たちの理想とすること 、私たちが変えてはならない大切にしたいものなのです 。

自分自身が変わらなければ、変わらない。

あなたが何もしなければ世界は何も変わらない 。何もね 。 U N L E S S s o m e o n e l i k e y o u c a r e s a w h o l e a w f u l l o t , n o t h i n g i s g o i n g t o g e t b e t t e r . I t ’ s n o t . ( D r . S e u s s 『ロラックスおじさんの秘密の種 』 )

 

まとめ

世界を変えるとは、自分自身の世界の見方を変えることだと思う。

それは、他者との関係性を観察することで、自分自身のナラティブに気づき、相手とのナラティブに溝があることを発見し、その溝に橋をかけ、新たな関係性を構築していくことなのだろう。

これまで私はこの本を通じて 、一貫して 「観察せよ 」 、つまり 「見えていないものを見よ 」ということを書き続けてきました 。そして 、他者との間に生じる適応課題の背後には 、 「私たちは見えていないことが何かが見えていない 」 、 「わかっていないことが何かがわかっていない 」という問題があることを述べてきました 。どんなに目を凝らしても 、私たちは自分のナラティヴのために 「見えていないこと 」があります 。それだけに 、自分のナラティヴからは見えていないものを見ようとすることは 、新たな関係性を構築していこうとする上で根本的に重要です 。

直近3本書いてきた読了記事で取り上げた書籍は、すべて組織における関係性に働きかける取り組みについての書籍だった。

 

 

 

 

これらの取り組みすべてが、組織内での適応課題を明らかにしたり解決したりして、新たな関係性を構築していくための取り組みと言える。

ただし、これらの取り組みもただ導入するだけでは意味がなく、まずは組織内のナラティブの溝を発見し、そこに橋をかけていくことを意識しなければ、それぞれの真の効果は得られないだろう。

【読了】「ザッソウ 結果を出すチームの習慣」

前置き

今回はこちらの書籍の感想。

 

ソニックガーデンの倉貫さんといえば『納品」をなくせばうまくいく』。当時、こんな受託開発のやり方があるんだと衝撃を受けた。

 

「納品」をなくせばうまくいく

「納品」をなくせばうまくいく

 

 

要約

タイトルの「ザッソウ」は「雑談+相談」を合わせた言葉。

冒頭では作業の効率化を求めすぎて失敗した後、雑談と相談の重要性に気づいたところか始まる。

イデアを生み出し 、成果を上げて結果を出すために必要なのは 、効率化だけを追求するのではなく 、気軽に雑談と相談ができるチ ームでいることこそが重要なのだと考えるようになりました 。

報告+連絡+相談=ホウレンソウは有名だが、なぜ「雑談」も必要なのか。

「相談はともかく 、なぜ雑談まで必要なのか ? 」そう不思議に思うかもしれませんが 、相談しながら雑談することもあれば 、雑談しているうちに相談になってアイデアが生まれることも多くあります 。相談と雑談のあいだに明確な境界線を引くことは難しいのです 。それに 、普段から雑談さえしていない関係で 、急に相談するとなると心理的なハ ードルがとても高くなります 。雑談できる関係性があるからこそ 、いつでも相談できるようになるわけです 。

「雑談」を普段から行って信頼関係の土台を作ることで、「相談」しやすくする状況を作るため。

相談ができないと、チームの生産性を下げる自体に発展してしまう恐れがある。

普段から話したこともないような相手だと 、相談の前に声をかけることすら緊張するものです 。 「こんなことなら 、わざわざ相談しなくていいかな … … 」と 、言い訳を考えてしまいます 。

相談ができないと 、大きな手戻りが発生する 、聞けば一瞬でわかることに時間をかけて調べてしまうなど 、チ ームの生産性を下げることになってしまいます 。相談できないことは個人の問題ではなく 、チ ームの問題なのです 。

効率化された情報共有だけでは、個人のモチベーションは保たれない。

効率化された情報共有だけを徹底的に追求すると 、短期的な成果は見込めますが 、その結果として個人のモチベ ーションが保たれず 、辞めていく人が出てきてしまうことになります 。そうなると長期的な視点で見たときに 、組織として成果を出し続けることは難しくなってしまいます 。

この書籍でも、「心理的安全性」の重要性が語られる。

心理的安全性 (たとえミスしても非難されない ) ・相互信頼 (仕事を最後までやりきってくれる ) ・構造と明確さ (有効な意思決定プロセスがある ) ・仕事の意味 (自分自身にとっても意義がある ) ・インパクト (どう貢献しているか理解している )

心理的安全性」とは、仲良しでいるということではない。

仲良しでいたいという理由で 、成果を出すために本来言うべきことを言わないでいるとしたら 、生産性の高いチ ームにはなりません 。

順番としては 、チ ーム内の心理的安全性が高まり 、成果を出すために意見を言い合えるようになることが先です 。たとえ仲良しでなくても 、真正面から遠慮なく意見をぶつけ合える関係性ができれば 、その次にきっと成果を出すことができるでしょう 。大きな成果を出すことができれば 、チ ームのメンバ ーがお互いをたたえ 、仲良くなるはずです 。結果としては仲の良いチ ームに見えるのですが 、それが狙いではないのです 。

報告と連絡よりも「相談」が重要なのはなぜか。

報告と連絡は対話がなくても成立しますが 、相談だけはそうはいきません 。というのも 、相談は伝えて終わりではなく 、未来に向けて内容を話し合う必要があります 。つまり 、ホウレンソウのうち相談だけが 、決まっていないことを議論して 、仕事を前へ進める役割があり 、それだけ大事だといえるのです 。

基本的にアイデアは 、その人の過去の経験から生まれます 。実体験に限らず読んだ本や見た映画なども含まれます 。そうした経験を蓄積した人生というインプットがあるからアイデアが出るのです 。逆をいえば 、その人が経験していないことからは何も生まれてきません 。そこで他の人に相談することで 、その人が経験したことや考えたことを新たなインプットにすることができるようになります 。

今の時代、どんなチームが成果を上げることが出来るのか。

チ ームの目的達成のために自分事で考え 、動くことのできる人たちで構成されたチ ームだと私は考えています 。さらに言えば 、仕事の責任を果たすために 、 「自分だけ仕事をすればいい 」と考えるのではなく 、 「チ ームの成果を最大化するために 、できることはなんでもする 」と考えられる人たちで構成されたチ ームなのではないかと思います

ザッソウは無理やり取り入れるものではない。

ザッソウの習慣をチ ームに広げていく際にもっとも重要なことは 、ザッソウそのものを無理やり取り入れないことです 。それよりも 、ザッソウが自然と生まれてくるような人間関係や環境に力を注ぐことから始めましょう 。

 

ザッソウには6つの効果がある。

  1. 助け合いのできる信頼関係が構築される
  2. 共通の価値観やカルチャーが醸成される
  3. 社員のキャリアや将来への不安が少なくなる
  4. 気軽なフィードバックで仕事の質と速度が向上する
  5. マニュアル化されにくい暗黙知が共有される
  6. 自分たちで判断して仕事を進められる社員が育つ

 

信頼関係は少しづつ溜まっていくもの。

F a c e b o o kをはじめとした S N Sなどで知り合いや友人とつながっている人も多いと思います 。それでも 、やはり普段からコミュニケ ーションを取っていない人に声をかけるのは勇気がいるものです 。頻繁にコメントでやりとりをし 、 「いいね ! 」を押し合っている人の方が話しかけやすいのではないでしょうか 。繰り返し接することで好意度や印象が高まる効果を 、心理学では 「単純接触効果 」や 「ザイアンスの法則 」といいます 。 C Mで流れる曲が売れる仕組みも同じです 。

 

「働きがい」と「働きやすさ」は異なってくるもので、ザッソウはそれら両方を高める。

働きがい :仕事を通じてやりがいを感じること

働きやすさ :仕事をしていくうえでの環境や制度に関すること

 

コミュニケーションは人にとっての癒しとなり、人間の本質を捉えていくことで成果は上がっていく。

そもそも人間同士のコミュニケ ーションは人にとっての癒しになります 。

 

「ザッソウ」しやすい職場をつくるには。

意図的にザッソウの内容の質を上げていく。

そこには4つの段階がある。

 

ゾーン1 他愛のない雑談

ゾーン2 交渉や合意形成

ゾーン3 問題解決の議論

ゾーン4 新しいアイデア

 

コラボレーションが生まれる関係を構築するには。

人が集まるだけではチームにならない。

人の集まりの関係性をレベル分けしてステップアップしていけるように考えられたのが「チームワークの7つの段階」。

ワークコラボレーション・レビュー | チームワークの7段階

 

レベルアップしていくことで、ザッソウが有益なものになっていく。そして本当のチームワークが生まれる。

レベル 4を経てレベル 5 ~ 7になると 、初めて有益なザッソウを実現することができます 。お互いの存在を認め合うことで相談したいと思うようになり 、信頼されて相談されると真摯に知恵を出そうとするものです 。それがレベル 5の段階です 。

「共通の目的の実現こそが最優先 」と共有されているので 、利害関係を越えて協力し合うこともいとわなくなりますし 、表現に配慮しつつも遠慮なく意見をぶつけ合うこともできます 。これこそが本当のチ ームワ ークです 。

しかし、レベル5まで進めてもそこから先に進めなくなってしまうこともある。そこでヒントになるのが「タックマンモデル」。

共通の目的は認識していて 、お互いのことを認め合って相談もしているけれど 、どこかで意見がぶつかることを恐れ 、それで言いたいことを飲み込んでしまうといったことが起きてしまっているのです 。その状態を乗り越えるヒントになるのが 、有名な 「タックマンモデル 」の考え方です 。

仕事ができる人は「正しい衝突」が超得意! | 30代から身につけたいキャリア力実戦講座 | 東洋経済オンライン | 経済ニュースの新基準

 

タックマンモデルにおける「ストーミング」の状態を乗り越えるには、タックマンモデル自体を共有してみる。

ここを乗り越えるポイントの 1つが 、このタックマンモデル自体をチ ームや関係者全員で共有することです 。スト ーミングの段階を乗り越えることでチ ームが一皮むけて次の段階に進めると信じることができれば 、生産性の低下も前向きに捉えることができるようになります 。

 

ザッソウしやすい心理的安全を高める9つの観点がある。

  1. チームの目標がはっきりしている
  2. 適度に話しやすい人数である
  3. 強みを知り、認め合っている
  4. 強みだけでなく、弱みもみせる
  5. プライベートなことも共有している
  6. 情報がオープンになっている
  7. 判断基準と価値観が共有されている
  8. リアクションの意識がそろっている
  9. 「肯定ファースト」と「NOと言うこと」

 

人間の多面的な部分を見ることで心理的安全性は高まる。

そもそも人間は多面的なものですが 、関係性が薄いとその人の一面しか見えていないことが多いのです 。そうした多面的な部分が見えるようになると心理的安全性は高まります 。これもニワトリとタマゴの問題で 、専門的には 「因果性のジレンマ 」というのですが 、それが起こるのです 。

人間の脳は自分の知らないことやわからないことに対してネガティブになってしまうので、情報をオープンにしないと心理的安全性は高まらない。

人間の脳は 、自分の知らないことやわからないことに対して 、ネガティブな想像で補完してしまう傾向にあるそうです 。

 

ザッソウできる職場にはゆとりがあり、ザッソウが溢れている。

ザッソウするためには業務改善を行い、ゆとりを作り必要がある。

業務改善には現在の業務を分析して「見える化」がポイントになるが、同時になんでも言い合える関係性を気付くための「言える化」も進める必要がある。

心理的安全性が低かったり 、目標やビジョンが共有されていない状態で聞き出そうとしても 、素直に答えてくれることはないでしょう 。 

アイスキャンディの 「ガリガリ君 」で知られる赤城乳業株式会社では 、社内の 「言える化 」を大事にすることで 、社員からのアイデアを引き出し 、自由闊達な組織をつくり上げているそうです 。 

赤城乳業の 「言える化 」を実現するための工夫の 1つが 、委員会やプロジェクトという形で縦割りの部署を超えた横串の活動を行うことです 。その委員会のリ ーダ ーに若手社員を抜擢することで 、意見を言える場をつくり出しています 。また 、全員参加の社員旅行であったり 、同期と映画やミュ ージカルを見る 「感性教育 」という制度に取り組むなど 、心の壁を取り払い 、お互いを知るための機会をつくっています 。これらも 「言える化 」できるようにするための取り組みということです 。

 

チームワークには人の喜びがある。

どんな仕事でもチームで成果を上げられるようになれば楽しくなってくるはず。

どんなにつらい状況になっても 、一緒に愚痴でも言い合いながら立ち向かう仲間がいれば 、乗り越えられる 。少なくとも 、そう思える希望があるなら不幸とまではいかないでしょう 。

どんな仕事だって 、目標に向かってチ ーム一丸となって成果を出していくことができれば楽しいものになるはずです 。

 

まとめ

本書籍の最も大事なことは、最後の以下の一文が顕著に表している。

ザッソウよりも大切なことは 、ザッソウが自然と生み出されるような環境をつくっていくことです 。

 

前々回の記事でOKR、

前回の記事で1on1に関する書籍の感想を書いたが、

共通して言えるのはメンバーが「所属」を実感出来ているような組織を作っていくことが、

組織のパフォーマンス向上の鍵になると言うこと。

OKR、1on1、ザッソウはいずれもwhy・how・howの部分。

whyを忘れてただ制度として始まるだけ始めてしまっても、それぞれの真の効果は得られないだろう。