Cara menggunakan inversi kawalan di C #

Kedua-dua penyongsangan kawalan dan suntikan kebergantungan membolehkan anda memutuskan pergantungan antara komponen dalam aplikasi anda dan menjadikan aplikasi anda lebih mudah untuk diuji dan diselenggara. Walau bagaimanapun, penyongsangan suntikan kawalan dan ketergantungan tidak sama - terdapat perbezaan yang halus antara keduanya.

Dalam artikel ini, kita akan mengkaji pembalikan corak kawalan dan memahami bagaimana perbezaannya dari suntikan ketergantungan dengan contoh kod yang relevan di C #.

Untuk bekerja dengan contoh kod yang disediakan dalam artikel ini, anda harus memasang Visual Studio 2019 di sistem anda. Sekiranya anda belum mempunyai salinannya, anda boleh memuat turun Visual Studio 2019 di sini. 

Buat projek aplikasi konsol di Visual Studio

Pertama, mari buat projek aplikasi konsol .NET Core di Visual Studio. Dengan mengandaikan Visual Studio 2019 dipasang di sistem anda, ikuti langkah-langkah yang digariskan di bawah untuk membuat projek aplikasi konsol .NET Core baru di Visual Studio.

  1. Lancarkan ID Studio Visual.
  2. Klik pada "Buat projek baru."
  3. Di tetingkap "Buat projek baru", pilih "Aplikasi Konsol (.NET Core)" dari senarai templat yang dipaparkan.
  4. Klik Seterusnya. 
  5. Di tetingkap "Konfigurasikan projek baru anda" yang ditunjukkan di sebelah, tentukan nama dan lokasi untuk projek baru.
  6. Klik Buat. 

Ini akan membuat projek aplikasi konsol .NET Core baru di Visual Studio 2019. Kami akan menggunakan projek ini untuk meneroka penyongsangan kawalan di bahagian seterusnya artikel ini.

Apakah penyongsangan kawalan?

Inversion of control (IoC) adalah corak reka bentuk di mana aliran kawalan program terbalik. Anda boleh memanfaatkan inversi corak kawalan untuk memutuskan komponen aplikasi anda, menukar pergantungan implementasi, tiruan tiruan, dan menjadikan aplikasi anda modular dan dapat diuji.

Suntikan ketergantungan adalah sebahagian daripada prinsip songsang kawalan. Dengan kata lain, suntikan kebergantungan adalah salah satu cara untuk melaksanakan inversi kawalan. Anda juga dapat melaksanakan pembalikan kawalan menggunakan peristiwa, perwakilan, pola templat, kaedah kilang, atau pencari perkhidmatan, misalnya.

Pembalikan corak reka bentuk kawalan menyatakan bahawa objek tidak boleh membuat objek yang bergantung kepada mereka untuk melakukan beberapa aktiviti. Sebaliknya, mereka harus mendapatkan benda-benda itu dari perkhidmatan luar atau bekas. Ideanya serupa dengan prinsip Hollywood yang mengatakan, "Jangan panggil kami, kami akan memanggil anda." Sebagai contoh, alih-alih aplikasi memanggil metode dalam kerangka, kerangka akan memanggil implementasi yang telah disediakan oleh aplikasi. 

Pembalikan contoh kawalan di C #

Andaikan anda membina aplikasi pemprosesan pesanan dan anda ingin melaksanakan pembalakan. Demi kesederhanaan, mari kita anggap bahawa sasaran log adalah fail teks. Pilih projek aplikasi konsol yang baru anda buat di tetingkap Solution Explorer dan buat dua fail, bernama ProductService.cs dan FileLogger.cs.

    ProductService kelas awam

    {

        FileLogger _fileLogger = readLon peribadi = FileLogger baru ();

        log kekosongan awam (mesej rentetan)

        {

            _fileLogger.Log (mesej);

        }

    }

    FileLogger kelas awam

    {

        log kekosongan awam (mesej rentetan)

        {

            Console.WriteLine ("Kaedah Log Inside dari FileLogger.");

            LogToFile (mesej);

        }

        kekosongan peribadi LogToFile (mesej rentetan)

        {

            Console.WriteLine ("Kaedah: LogToFile, Teks: {0}", mesej);

        }

    }

Pelaksanaan yang ditunjukkan dalam coretan kod sebelumnya adalah betul tetapi ada batasannya. Anda terpaksa memasukkan data ke fail teks sahaja. Anda tidak boleh log data ke sumber data lain atau sasaran log yang berbeza.

Pelaksanaan pembalakan yang tidak fleksibel

Bagaimana jika anda mahu log data ke jadual pangkalan data? Pelaksanaan yang ada tidak akan menyokong ini dan anda akan terpaksa mengubah pelaksanaannya. Anda boleh mengubah pelaksanaan kelas FileLogger, atau anda boleh membuat kelas baru, katakanlah, DatabaseLogger.

    Pangkalan Data kelas awam

    {

        log kekosongan awam (mesej rentetan)

        {

            Console.WriteLine ("Kaedah Inside Log dari DatabaseLogger.");

            LogToDatabase (mesej);

        }

        logToDatabase kekosongan peribadi (mesej rentetan)

        {

            Console.WriteLine ("Kaedah: LogToDatabase, Teks: {0}", mesej);

        }

    }

Anda mungkin membuat contoh kelas DatabaseLogger di dalam kelas ProductService seperti yang ditunjukkan dalam coretan kod di bawah.

ProductService kelas awam

    {

        FileLogger _fileLogger = readLon peribadi = FileLogger baru ();

        DatabaseLogger yang dibaca peribadi _databaseLogger =

         DatabaseLogger baru ();

        kekosongan awam LogToFile (mesej rentetan)

        {

            _fileLogger.Log (mesej);

        }

        membatalkan logToDatabase awam (mesej rentetan)

        {

            _fileLogger.Log (mesej);

        }

    }

Namun, walaupun ini akan berjaya, bagaimana jika anda perlu memasukkan data aplikasi anda ke EventLog? Reka bentuk anda tidak fleksibel dan anda akan terpaksa menukar kelas ProductService setiap kali anda perlu log masuk ke sasaran log baru. Ini bukan sahaja membebankan tetapi juga akan menjadikan anda sangat sukar untuk menguruskan kelas ProductService dari masa ke masa.

Tambahkan fleksibiliti dengan antara muka 

Penyelesaian untuk masalah ini adalah dengan menggunakan antara muka yang akan dilaksanakan oleh kelas pembalak konkrit. Coretan kod berikut menunjukkan antara muka yang dipanggil ILogger. Antaramuka ini akan dilaksanakan oleh dua kelas konkrit FileLogger dan DatabaseLogger.

antara muka awam ILogger

{

    batal Log (mesej rentetan);

}

Versi kemas kini kelas FileLogger dan DatabaseLogger diberikan di bawah.

kelas awam FileLogger: ILogger

    {

        log kekosongan awam (mesej rentetan)

        {

            Console.WriteLine ("Kaedah Log Inside dari FileLogger.");

            LogToFile (mesej);

        }

        kekosongan peribadi LogToFile (mesej rentetan)

        {

            Console.WriteLine ("Kaedah: LogToFile, Teks: {0}", mesej);

        }

    }

DatabaseLogger kelas awam: ILogger

    {

        log kekosongan awam (mesej rentetan)

        {

            Console.WriteLine ("Kaedah Inside Log dari DatabaseLogger.");

            LogToDatabase (mesej);

        }

        logToDatabase kekosongan peribadi (mesej rentetan)

        {

            Console.WriteLine ("Kaedah: LogToDatabase, Teks: {0}", mesej);

        }

    }

Anda kini boleh menggunakan atau mengubah pelaksanaan konkrit antara muka ILogger bila perlu. Coretan kod berikut menunjukkan kelas ProductService dengan pelaksanaan kaedah Log.

ProductService kelas awam

    {

        log kekosongan awam (mesej rentetan)

        {

            ILogger logger = FileLogger baru ();

            logger.Log (mesej);

        }

    }

Setakat ini, begitu baik. Walau bagaimanapun, bagaimana jika anda ingin menggunakan DatabaseLogger sebagai pengganti FileLogger dalam kaedah Log kelas ProductService? Anda boleh mengubah pelaksanaan kaedah Log di kelas ProductService untuk memenuhi syarat, tetapi itu tidak menjadikan reka bentuknya fleksibel. Mari buat reka bentuk lebih fleksibel dengan menggunakan penyongsangan kawalan dan suntikan ketergantungan.

Balikkan kawalan menggunakan suntikan kebergantungan

Coretan kod berikut menggambarkan bagaimana anda dapat memanfaatkan suntikan kebergantungan untuk melewati contoh kelas penebang konkrit menggunakan suntikan konstruktor.

ProductService kelas awam

    {

        ILogger _logger peribadi baca;

        Perkhidmatan Produk awam (ILogger logger)

        {

            _logger = pembalak;

        }

        log kekosongan awam (mesej rentetan)

        {

            _logger.Log (mesej);

        }

    }

Terakhir, mari kita lihat bagaimana kita dapat menyampaikan pelaksanaan antara muka ILogger ke kelas ProductService. Coretan kod berikut menunjukkan bagaimana anda dapat membuat contoh kelas FileLogger dan menggunakan suntikan konstruktor untuk melepasi kebergantungan.

kekosongan statik Utama (string [] args)

{

    ILogger logger = FileLogger baru ();

    ProductService productService = ProductService baru (logger);

    productService.Log ("Hello World!");

}

Dengan berbuat demikian, kami telah menukar kawalan. Kelas ProductService tidak lagi bertanggungjawab untuk membuat contoh pelaksanaan antara muka ILogger atau bahkan memutuskan pelaksanaan antara muka ILogger mana yang harus digunakan.

Inversi kawalan dan suntikan ketergantungan membantu anda dengan instansiasi automatik dan pengurusan kitaran hidup objek anda. ASP.NET Core merangkumi penyongsangan bekas kawalan yang ringkas dan terbina dalam dengan sekumpulan ciri terhad. Anda boleh menggunakan wadah IoC terbina dalam ini jika keperluan anda sederhana atau menggunakan bekas pihak ketiga jika anda ingin memanfaatkan ciri tambahan.

Anda boleh membaca lebih lanjut mengenai cara bekerja dengan penyongsangan kawalan dan suntikan ketergantungan dalam ASP.NET Core dalam catatan saya yang terdahulu di sini.