ใช้ Lazy ของ .NET 4 สร้าง dependency ช่วยประหยัด main memory กันเถอะ!

เริ่มต้นผมเขียน code แบบนี้

    public class AgileDeveloper
    {
        IBuildable _Buildable;
        ICodeQuality _CodeQuality;
        IDeploy _Deploy;
        ITest _Test;

        public AgileDeveloper()
        {
            _Buildable = new JenkinBuild();
            _CodeQuality = new Sonarqube();
            _Deploy = new JenkinDeploy();
            _Test = new FitnesseTest();
        }

        public SourceCode Coding(IList userStories)
        {
            throw new NotImplementedException(); 
        }

        public IBuildable Buildable 
        {
            get
            {
                return _Buildable;
            }
        }
        public ICodeQuality CodeQuality 
        {
            get
            {
                return _CodeQuality;
            }
        }
        public IDeploy Deploy
        {
            get
            {
                return _Deploy;
            }
        }
        public ITest Test
        {
            get
            {
                return _Test;
            }
        }
    }

ที่ไฮไลท์สีแดงนั่นคือ dependency อันได้แก่ IBuildable, ICodQuality, IDeploy และ ITest ของ class AgileDeveloper และเพื่อให้ client หรือผู้ใช้ class ของผมนี้สร้างง่ายๆผมจึง สร้าง instance สำหรับ dependency เหล่านั้นไว้ที่ constructor ของผม ตรงส่วนของ ไฮไลท์สีเขียว

ตอนใช้งาน ในบางสถานการณ์ส่วนใหญ่ ก็จะประมาณนี้

          var chavp = new AgileDeveloper();
          var johnFarmer = new Customer();
          var mikeCohn = new ProductOwner();
          var userStories = mikeCohn.CreateUserStories(johnFarmer);
          var sourceCode = chavp.Coding(userStories);
          var software = chavp.Buildable.Build(sourceCode);
          chavp.Deploy.OnUAT(software);
          if (chavp.Test.AcceptanceTest(userStories, software))
          {
              // Done
              chavp.Deploy.OnProduction(software);
          }

ส่วนที่ไฮไลท์สีแดงข้างบ้นนั้น เพื่อบอกคุณว่าผม ได้เรียกใช้ dependency ที่จุดต่างๆ มันจะไม่เกิดปัญหา null pointer exception แน่นอน เพราะผมได้สร้างพวกมันไว้ ทั้งหมดแต่ต้นแล้ว

ต่อมาผมได้ review code ของผมใหม่แล้วพบว่า dependency ICodeQuality ที่ผมสร้างไว้นั้น ไม่ถูกใช้ประโยชน์เลยตามสถานการณ์ตัวอย่างข้างต้น แต่ว่าผมมีความรู้เรื่อง Lazy Initialization มาบ้าง และ โชคดีที่ .NET 4.0 ได้จัดเตรียม Lazy object มาให้เราเพิ่มคุณสมบัติ Lazy Initialization ได้ง่ายๆแล้ว

Lazy Initialization ก็คือ คุณสมบัติที่จะสร้าง instance ก็ต่อเมื่อ client ต้องการใช้เท่านั้น หรือเรียกว่าสร้าง OnDemand ผมจึงกลับไป refactoring code ของผม โดย ใช้ Lazy object ของ .NET 4 ที่จัดเตรียมไว้ให้แล้ว เมื่อ refactor เสร็จแล้วจะได้ code แบบนี้

    
    public class AgileDeveloper
    {
        Lazy _Buildable;
        Lazy _CodeQuality;
        Lazy _Deploy;
        Lazy _Test;

        public AgileDeveloper()
        {
            _Buildable = new Lazy(() => new JenkinBuild());
            _CodeQuality = new Lazy(() => new Sonarqube());
            _Deploy = new Lazy(() => new JenkinDeploy());
            _Test = new Lazy(() => new FitnesseTest());
        }

        public SourceCode Coding(IList userStories)
        {
            throw new NotImplementedException(); 
        }

        public IBuildable Buildable 
        {
            get
            {
                return _Buildable.Value;
            }
        }
        public ICodeQuality CodeQuality 
        {
            get
            {
                return _CodeQuality.Value;
            }
        }
        public IDeploy Deploy
        {
            get
            {
                return _Deploy.Value;
            }
        }
        public ITest Test
        {
            get
            {
                return _Test.Value;
            }
        }
    }

ดูจุดที่ผมไฮไลท์สีแดงจาก code ข้างบนเป็นจุดที่ผมเปลี่ยน code โดยเพิ่ม Lazy เข้าไปตรง วัตถุที่ ผมต้องการสร้างมันแบบ lazy ซึ่ง Lazy ถูกบรรจุอยู่ใน package System ของ .NET 4 อยู่แล้ว แล้วก็ไปเปลี่ยน ตรงสร้าง โดยใช้ lamda expression เพื่อกำหนด instance ตามต้องการในส่วนของ constructor และสุดท้าย แก้ไขที่ getter method โดยเพิ่ม property Value เข้าไปเพื่อ get ค่า instance ตามที่เราต้องการ

class AgileDeveloper ของผมก็จะมีคุณสมบัติ Lazy Initialization แล้วซึ่งจะช่วยประหยัด main memory ไปได้สำหรับสถานะการณ์ที่ไม่ได้ใช้ประโยชน์ dependency ตัวนั้น ตัวอย่างสถานการณ์เช่น

 var chavp = new AgileDeveloper();
 var legacySourceCode = GithubRepository.Clone("https://github.com/chavp/xxx.git");
 chavp.CodeQuality.CodeCoverage(legacySourceCode);

ตาม code สถานการณ์ข้างบน เพียงแค่ใช้ class AgileDeveloper เพื่อทำ Code Quality กับ legacy code เพียงเท่านั้น จึงไม่มีการสร้าง instance ของ  IBuildable, IDeploy และ ITest เตรียมไว้สำหรับสถานการณ์เพียงเท่านี้เลย ซึ่งจะช่วยลดการใช้ main memory ได้อีก

นี่เป็นการใช้ class Lazy ของ .NET 4 เพื่อเพิ่มประสิทธิภาพของโปรแกรมวิธีง่ายๆแบบหนึ่ง ยังมีวิธีที่เพิ่มคุณสมบัติของ Lazy Initialization ได้ด้วยเทคนิคของ Dependency Injection(DI) โดยจะใช้คุณสมบัติของ DI Container ของผู้พัฒนาไว้เพื่อ กำหนดว่า dependency ตัวไหนต้องการให้เป็นแบบ  Lazy Initialization ถ้ามีเวลาผมจะเอามานำเสนอให้อ่านกันครับ

หวังว่าบทความนี้จะเป็นประโยชน์กับเพื่อนๆโปรแกรมเมอร์ครับ

ขอบคุณครับ

#:P

Advertisements