Saturday, August 6, 2011

Enterprise Software Architecture : I


(မွတ္ခ်က္။ ဒီအပိုင္းကို ေကာင္းေကာင္းနားလည္ သေဘာေပါက္နိုင္ဖို့ အတြက္ Dependency Injection Design Pattern ကို အရင္ဆံုး သိရိွနားလည္သေဘာေပါက္ျပီးျဖစ္သင့္ပါတယ္။)

အပိုင္း-၁
Constructor Dependency Injection
[Service provider class ၏ Constructor သို့၎အတြက္ လိုအပ္ေသာ Object မ်ားေပးသြင္းျခင္း]


ဆိုပါစို့ ကြ်န္ေတာ္တို့ဟာ အခု ကမၻာလွည့္ ခရီးသြားလာေရးလုပ္ငန္း တစ္ခုအတြက္ Application Service တခုေရးမယ္ဆိုပါစို ့ဗ်ာ။ အလြယ္ဆံုး အရိုးရွင္းဆံုး simple and plain structure ကိုပဲ စျပီး စဉ္းစားၾကည့္ၾကစို ့။ service name ကို TouristInformationService လို ့ အမည္ေပးလိုက္တာေပါ့။

၁။ ဒီ service မွာ ေနရာေဒသ Location ကို ျပမယ့္ ေျမပံု ပါမယ္။
၂။ ျပီးရင္ စိတ္ဝင္စားစရာ ေနရာေတြ အေၾကာင္းပါမယ္။ ဒါပဲ။


ကဲ အဲဒီေတာ့ ပထမဆံုး လိုအပ္တဲ့ ေျမပံု class ကို model အေနနဲ ့ create လုပ္လိုက္ဗ်ာ။ [ဘာေၾကာင့္ Interface ကို သံုးတာလဲ...ဒီ class ဟာ view လား model လား controller/service လား ဆိုတာေတြကို ေတာ့ သိျပီးျဖစ္မယ္လို ့ ေမွ်ာ္လင့္ပါတယ္။]


namespace IoCContainer.Model.Maps
{
    public interface IMap
    {
        void Set(Location currentLocation);
        void Add(PlaceOfInterests.PlaceOfInterest placeOfInterest);
        bool CurrentLocationIsNear(PlaceOfInterests.PlaceOfInterest placeOfInterest);
    }
}

namespace IoCContainer.Model.Maps
{
    public class SatelliteMap : IMap
    {
        public void Set(Location currentLocation)
        {
       
        }

        public void Add(PlaceOfInterests.PlaceOfInterest placeOfInterest)
        {
       
        }

        public bool CurrentLocationIsNear(PlaceOfInterests.PlaceOfInterest placeOfInterest)
        {
            return true;
        }
    }
}

ဒါကေတာ့ ဆိုပါစို ့Utility class တခုေပါ့ဗ်ာ။
namespace IoCContainer.Model
{
    public class Location
    {
    }
}
ျပီးရင္ သိတဲ့အတိုင္း ကြ်န္ေတာ္တို ့အထက္မွာ ေဆြးေနြးခဲ့သလိုပဲ စိတ္ဝင္စားဖြယ္ေနရာေတြကို စုစည္း ေပးမယ့္ class တစ္ခု create လုပ္ဖို ့လိုတာေပါ့။ အင္း..သူကလည္း data (database) နဲ ့ ဆိုင္တဲ့ေကာင္ဆိုေတာ့ ထံုးစံအတိုင္း model အုပ္စုထဲမွာ ပဲေပါ့ဗ်ာ။ ထားပါ။

namespace IoCContainer.Model.PlaceOfInterests
{
    public interface IPlaceOfInterestDictionary
    {
        IList<PlaceOfInterest> Places();
    }
}

အထူးသတိထားဖို ့က အခုကြ်န္ေတာ္တို ့ေရးေနတာက၊ Enterprise level application တစ္ခုအတြက္ လိုအပ္တဲ့ component တစ္ခု၊ service တစ္ခုကိုေရးေနတာ။ ဒီ service ကို တကမၻာလံုးသံုး လို ့ အဆင္ေျပရမယ္ ဆိုတာ မေမ့ပါနဲ ့။ ျမန္မာျပည္ကိုေရာက္ရင္ ျမန္မာျပည္ေျမပံု ျမန္မာျပည္ကစိတ္ဝင္စားစရာ ေကာင္းတဲ့ ေနရာေတြ၊ UK ေရာက္ရင္ UK ေျမပံု နဲ ့ UK ကစိတ္ဝင္စားစရာ ေကာင္းတဲ့ ေနရာေတြ၊ စသည္ျဖင့္ သက္ဆိုင္ရာ သက္ဆိုင္ရာ ကို execute လုပ္ေပးနိုင္ရပါမယ္။ ဒါေၾကာင့္....Maps ေတြ PlaceOfInterest ေတြ ဟာ မို်းကဲြေပါင္း မ်ားစြာ ပါမယ္ဆိုတာ သတိျပုပါ။

namespace IoCContainer.Model.PlaceOfInterests
{
    public class PlaceOfInterest
    {
    }
}

namespace IoCContainer.Model.PlaceOfInterests
{
    public class EnglishPlaceOfInterestDictionary : IPlaceOfInterestDictionary
    {
        public IList<PlaceOfInterest> Places()
        {
            return new List<PlaceOfInterest>();
        }
    }
}


ကဲအခု ကြ်န္ေတာ္တို ့ အေပၚမွာေရးခဲ့ျပီးျဖစ္တဲ့ ေျမပံုေတြ၊ သက္ဆိုင္စိတ္ဝင္စားစရာေနရာေတြကို လိုအပ္သလို စီမံခန္ ့ခဲြေပးမယ့္ service class တခု ေရးေတာ့မယ္ဗ်ာ။
web, web service, i-phone, android ..စတာတြမွာ run မယ့္ client application ေတြ ၊ တနည္းေျပာရရင္ ဒီ service class ကို သံုးစဲြမယ့္ service consumer class ေတြ က လွမ္း ေခၚမယ့္၊ လွမ္းသံုးမယ့္ class ေပါ့။

using IoCContainer.Model.Maps;
using IoCContainer.Model.PlaceOfInterests;
namespace IoCContainer.Model
{
    public class TouristInformationService
    {
        private IMap _map;
        private IPlaceOfInterestDictionary _placeOfInterestDictionary;


        public TouristInformationService(IMap map, IPlaceOfInterestDictionary placeDic)
        {
            this._map = map;
            this._placeOfInterestDictionary = placeDic;
        }
    }
}

ဒီ TouristInformationService class ထဲမွာ tourist information နဲ ့ ပတ္သက္တဲ့ function ေတြ အမ်ားၾကီးရိွမွာေပါ့ဗ်ာ။ ထားပါ..။ ဒါကေတာ့ auto understood ေပါ့။ အဲဒီ function ေတြကို ေခၚခ်င္ရင္ ဘာလုပ္ရမလဲ။ TouristInformationService object ကို create လုပ္ရမွာေပါ့။

ဘယ္သူကလုပ္မလဲ။ ေခၚမယ့္သူကလုပ္ရမွာေပါ့။
ဘယ္သူကေခၚမွာလဲ။ client ကေပါ့။

ဒါေၾကာင့္ client class မွာ TouristInformationService class ရဲ့ object ကို instantiate/create လုပ္ရမွာေပါ့။
ဒီမွာၾကည့္ဗ်ာ...

using IoCContainer.Model;
using IoCContainer.Model.Maps;
using IoCContainer.Model.PlaceOfInterests;

namespace IoCContainer.ServiceConsumer
{
    public class ClientThatUsesService
    {
        TouristInformationService touInfoService;
        //..
        //..
        //..
        //..
        public void initializeServiceObject()
        {
            IMap _map = new SatelliteMap();
            IPlaceOfInterestDictionary _placeOfInt = new EnglishPlaceOfInterestDictionary();

            touInfoService = new TouristInformationService(_map, _placeOfInt);
            //..
            //..
            //..
        }

        //..
        //..
        //..
    }
}
ကဲ..မိတ္ေဆြတို ့ ျမင္တဲ့အတိုင္းပဲ.

ကိုယ္သံုးမယ့္ TouristInformationService ရဲ့ object ကို instantiate/create လုပ္တဲ့အခါ
ေခၚတဲ့ သံုးတဲ့ client က
TouristInformationService အတြက္ လိုအပ္ေနတဲ့ ၊ TouristInformationService ကမီွခိုေနရတဲ့
ေျမပံုတို ့[Map] စိတ္ဝင္စားစရာေနရာတို ့[PlaceOfInterest] ဆိုတဲ့ object ေတြကို
TouristInformationService Constructor ကတဆင့္ လွမ္း ျပီး ထည့္သြင္းေပးလိုက္လို ့
ဒီလို Architecture ကို
Constructor Dependency Injection လို ့ေခၚတာပါ။

ကဲေကာင္းပါျပီ..
ဒါဆို ဘာျဖစ္လဲေပါ့...။
ကြ်န္ေတာ္တို့ဟာ ဒီ ေဆာ့ဖ္ဝဲကို ဒီဇိုင္းခ်တုန္းက
- service module ေတြ အခ်င္းခ်င္း loosely coupled ျဖစ္ေစဖို့
- service module တစ္ခုခ်င္းစီကို testing လုပ္နိုင္ဖို ့
- maintenance/ enhancement လုပ္တဲ့အခါ module တခုက တခုကို မထိခိုက္ေစပဲ တခုခ်င္းစီကို လြယ္လြယ္ကူကူ ျမန္ျမန္ဆန္ဆန္ လုပ္နိုင္ဖို ့ ဆိုတဲ့ ရည္ရြယ္ခ်က္ေတြနဲ့ လုပ္ခဲ့တာေလ..။

ခုေတာ့ ဒါေတြ သြားျပီေပါ့..။
ဘာေၾကာင့္လဲ....
Client class ဟာ Service class ရဲ့ လိုအပ္တဲ့ module ေတြ class ေတြနဲ ့ tightly coupled ျဖစ္ေနျပီေလ။
၁-ထိပ္ဆံုးက using statement ကိုၾကည့္လိုက္ ။ လိုတဲ့ assembly ေတြကို reference လုပ္ရတယ္။
၂- ျပီးေတာ့လည္း service class ရဲ့ dependency ျဖစ္ေနတဲ့ Map တို ့ PlaceOfInterest တို ့ရဲ့ object ေတြကို ပါ create လုပ္ေပးရတယ္။
၃- ဒါေၾကာင့္ SatelliteMap တို ့ EnglishPlacesOfInterestDictionary class တို ့မွာ တခုခု ေျပာင္းလိုက္တာနဲ ့ client code က break ျဖစ္သြားျပီ..။
၄- ဒါ့အျပင္ SatelliteMap တို ့ EnglishPlacesOfInterestDictionary တို ့ရဲ့ implementation ကို change ခ်င္ရင္ [ဆိုပါစို ့ဗ်ာ USASatelliteMap နဲ ့ USAPlacesOfInterestDictionary ကို ေျပာင္းသံုးမယ္ဆိုရင္] ဒီ Tourist Service ကို သံုးမယ့္ client code ေနရာတိုင္းမွာ လိုက္ change ေနရမွာေပါ့..။

ဒါဆိုရင္ ကိုယ့္ရဲ့ client code က ကိုယ္သံုးမယ့္ service အေၾကာင္းပါမက ကိုယ္သံုးမယ့္ service ရဲ့ dependency ေတြကိုပါ သိဖို ့လိုအပ္ေနျပီေကာဗို် ့....။ tightly coupled ဆိုတာ ဒါကိုေျပာတာေပါ့။

ဒါဆိုရင္ ဒီအခက္အခဲ ကို ဘယ္လိုေက်ာ္လွြားမလဲဆိုတာကို Enterprise Software Architecture : II ရဲ့ Factory Pattern မွာ ဆက္လက္တင္ျပပါ့မယ္။

ရွြင္လန္းခ်မ္းေျမ့ပါေစ။


Reference:
Professional Enterprise .NET [2009 - Wiley Publishing, Inc., Indianapolis, Indiana, USA] by Jon Arking and Scott Millett

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.