Gevşek Birleştirilmiş Kod İçin Arabirimleri Kullanma


10

Arka fon

Belirli bir donanım aygıtının kullanımına bağlı bir projem var, ancak bu donanım aygıtını yapmam gereken şeyi yaptığı sürece kimin yaptığı önemli değil. Bununla birlikte, aynı şeyi yapması gereken iki cihazın bile aynı üretici tarafından yapılmadığında farklılıkları olacaktır. Bu yüzden uygulamayı ilgili marka / modelden ayırmak için bir arayüz kullanmayı düşünüyorum ve bunun yerine arayüzün en üst düzey işlevselliği kapsamasını sağladım. İşte benim mimarimin şöyle görüneceğini düşünüyorum:

  1. Bir C # projesinde bir arayüz tanımlayın IDevice.
  2. Cihazı temsil etmek için kullanılacak başka bir C # projesinde tanımlanan bir kütüphanede bir somut var.
  3. Beton cihazının IDevicearayüzü uygulamasını sağlayın.
  4. IDeviceArayüz gibi yöntemler olabilir GetMeasurementveya SetRange.
  5. Uygulamanın beton hakkında bilgi sahibi olmasını sağlayın ve betonu cihazı kullanan ( uygulamayan ) uygulama koduna iletin IDevice.

Bunun bunun için doğru yol olduğundan eminim, çünkü o zaman uygulamayı etkilemeden hangi cihazın kullanıldığını değiştirebileceğim (bazen gerçekleşiyor gibi görünüyor). Başka bir deyişle, betonun uygulamalarının nasıl uygulandığı GetMeasurementveya SetRangegerçekten nasıl çalıştığı önemli değildir (cihazın üreticileri arasında farklılık gösterebilir).

Aklımdaki tek şüphe, hem uygulamanın hem de cihazın somut sınıfının, hem IDevicearabirimi içeren kütüphaneye bağlı olmasıdır . Ama bu kötü bir şey mi?

Cihaz IDeviceaynı ad alanında değilse, uygulamanın cihaz hakkında nasıl bilmesi gerekmediğini de göremiyorum .

Soru

Uygulamam ve kullandığı cihaz arasındaki bağımlılığı çözmek için bir arayüz uygulamak için doğru bir yaklaşım gibi mi görünüyor?


Bunu yapmanın kesinlikle doğru yolu budur. Temel olarak bir aygıt sürücüsünü programlıyorsunuz ve bu, aygıt sürücülerinin geleneksel olarak nasıl yazıldığını, iyi bir sebeple gösteriyor. Bir cihazın yeteneklerini kullanmak için, bu yetenekleri en az soyut bir şekilde bilen koda güvenmeniz gerektiği gerçeğini ortadan kaldıramazsınız.
Kilian Foth

@KilianFoth Evet bir duygum vardı, soruma bir parça eklemeyi unuttum. Bkz.
Snoop

Yanıtlar:


5

Bence ayrıştırılmış yazılımın nasıl çalıştığını çok iyi anladınız :)

Aklımdaki tek şüphe, hem uygulamanın hem de cihazın somut sınıfının, hem IDevice arayüzünü içeren kütüphaneye bağlı olmasıdır. Ama bu kötü bir şey mi?

Olmasına gerek yok!

Cihaz ve IDevice aynı ad alanında değilse, uygulamanın cihaz hakkında nasıl bilmesi gerekmediğini de göremiyorum.

Tüm bu endişeleri Proje Yapısı ile başa çıkabilirsiniz .

Ben tipik olarak bunu yapmak:

  • Tüm soyut eşyalarımı bir Commonprojeye koy . Gibi bir şey MyBiz.Project.Common. Diğer projeler buna referansta özgürdür, ancak diğer projelere atıfta bulunmayabilir.
  • Bir soyutlamanın somut bir uygulamasını yarattığımda onu ayrı bir projeye koydum. Gibi bir şey MyBiz.Project.Devices.TemperatureSensors. Bu proje projeye referans verecektir Common.
  • Sonra Clientbenim uygulama (benim gibi bir şey MyBiz.Project.Desktop) giriş noktası olan projem var . Başlangıçta uygulama, soyutlama / somut uygulama eşlemelerini yapılandırdığım bir Bootstrapping işleminden geçer . Benim beton örneğini IDevicesgibi WaterTemperatureSensorve IRCameraTemperatureSensorburada, yoksa sonradan benim için doğru beton türlerini örneğini Fabrikalar veya bir IoC konteyner gibi hizmetleri yapılandırabilirsiniz.

Buradaki anahtar şey Client, hem Commonprojenizin hem soyut projenin hem de tüm somut uygulama projelerinin farkında olması gerektiğidir . Özet-> somut eşlemeyi Bootstrap kodunuzla sınırlayarak, uygulamanızın geri kalanının somut türlerden mutsuz bir şekilde habersiz olmasını mümkün kılarsınız.

Gevşek bir şekilde kod ftw :)


2
DI buradan doğal olarak takip ediyor. Bu büyük ölçüde bir proje / kod yapısı kaygısıdır. IoC konteynerine sahip olmak DI ile yardımcı olabilir, ancak bu bir önkoşul değildir. Bağımlılıkları manuel olarak da enjekte ederek DI elde edebilirsiniz!
MetaFight

1
Uygulama içerisindeki Foo nesne bir iDevice gerektiriyorsa Evet @StevieV, bağımlılık inversiyon yapıcı (yoluyla enjekte olarak basit olarak olabilir new Foo(new DeviceA());) ziyade Foo örneğini özelliği cihazınızın kendisi (içinde özel bir alana sahip private IDevice idevice = new DeviceA();) - hala olarak, DI ulaşmak Foo ilk durumda DeviceA'nın farkında değil
anotherdave

2
@anotherdave sizin ve MetaFight girişi çok yardımcı oldu.
Snoop

1
Eğer zaman olsun @StevieV, burada intro bir güzel Bahar gibi Kontrol / bağımlılığı olan kişilerin bu Enjeksiyon çerçevesi, bir Inversion (veya başka daha fazla ayrı, ( "Bob Amca" Martin, bunu icat adam) bir kavram olarak Bağımlılık Inversion içine
C♯

1
@anotherdave Kesinlikle kontrol etmeyi planlıyorum, tekrar teşekkür ederim.
Snoop

3

Evet, doğru yaklaşım gibi görünüyor. Hayır, özellikle uygulamanın kontrolü sizde ise uygulama ve cihaz kütüphanesinin arayüze bağlı olması kötü bir şey değildir.

Cihazların her zaman arayüzü uygulayamayacağından endişe ediyorsanız, hangi nedenle olursa olsun, adaptör desenini kullanabilir ve cihazların somut uygulamasını arayüze uyarlayabilirsiniz.

DÜZENLE

Beşinci endişenizi giderirken, böyle bir yapı düşünün (cihazlarınızı tanımlamanın kontrolünde olduğunuzu varsayıyorum):

Bir çekirdek kütüphaneniz var. İçinde IDevice adlı bir arayüz var.

Cihaz kitaplığınızda çekirdek kitaplığınıza bir referansınız var ve hepsi IDevice'i uygulayan bir dizi cihaz tanımladınız. Ayrıca farklı türde IDevices oluşturmayı bilen bir fabrikanız da var.

Uygulamanıza çekirdek kütüphaneye ve cihaz kütüphanesine referanslar dahil edersiniz. Uygulamanız artık IDevice arabirimine uyan nesnelerin örneklerini oluşturmak için fabrikayı kullanıyor.

Bu, endişelerinizi gidermek için birçok olası yoldan biridir.

ÖRNEKLER:

namespace Core
{
    public interface IDevice { }
}


namespace Devices
{
    using Core;

    class DeviceOne : IDevice { }

    class DeviceTwo : IDevice { }

    public class Factory
    {
        public IDevice CreateDeviceOne()
        {
            return new DeviceOne();
        }

        public IDevice CreateDeviceTwo()
        {
            return new DeviceTwo();
        }
    }
}

// do not implement IDevice
namespace ThirdrdPartyDevices
{

    public class ThirdPartyDeviceOne  { }

    public class ThirdPartyDeviceTwo  { }

}

namespace DeviceAdapters
{
    using Core;
    using ThirdPartyDevices;

    class ThirdPartyDeviceAdapterOne : IDevice
    {
        private ThirdPartyDeviceOne _deviceOne;

        // use the third party device to adapt to the interface
    }

    class ThirdPartyDeviceAdapterTwo : IDevice
    {
        private ThirdPartyDeviceTwo _deviceTwo;

        // use the third party device to adapt to the interface
    }

    public class AdapterFactory
    {
        public IDevice CreateThirdPartyDeviceAdapterOne()
        {
            return new ThirdPartyDeviceAdapterOne();
        }

        public IDevice CreateThirdPartyDeviceAdapterTwo()
        {
            return new ThirdPartyDeviceAdapterTwo();
        }
    }
}

namespace Application
{
    using Core;
    using Devices;
    using DeviceAdapters;

    class App
    {
        void RunInHouse()
        {
            var factory = new Factory();
            var devices = new List<IDevice>() { factory.CreateDeviceOne(), factory.CreateDeviceTwo() };
            foreach (var device in devices)
            {
                // call IDevice  methods.
            }
        }

        void RunThirdParty()
        {
            var factory = new AdapterFactory();
            var devices = new List<IDevice>() { factory.CreateThirdPartyDeviceAdapterOne(), factory.CreateThirdPartyDeviceAdapterTwo() };
            foreach (var device in devices)
            {
                // call IDevice  methods.
            }
        }
    }
}

Peki bu uygulama ile beton nereye gidiyor?
Snoop

Sadece yapmayı tercih ettiğim şey için konuşabilirim. Cihazların kontrolü sizde ise, beton cihazları kendi kütüphanelerine koymaya devam edersiniz. Aygıtların denetiminde değilseniz, bağdaştırıcılar için başka bir kitaplık oluşturursunuz.
Fiyat Jones

Sanırım tek şey, uygulama ihtiyacım olan betona nasıl erişecek?
Snoop

Bir çözüm, IDevices oluşturmak için soyut fabrika modelini kullanmak olacaktır.
Fiyat Jones

1

Aklımdaki tek şüphe, hem uygulamanın hem de cihazın somut sınıfının, hem IDevice arabirimini içeren kütüphaneye bağlı olmasıdır. Ama bu kötü bir şey mi?

Bunun tersini düşünmelisin. Bu şekilde gitmezseniz, uygulamanın tüm farklı aygıtlar / uygulamalar hakkında bilgi sahibi olması gerekir. Unutmamanız gereken şey, bu arayüzün değiştirilmesinin zaten vahşi doğada uygulamanıza zarar verebileceğidir, bu nedenle söz konusu arayüzü dikkatlice tasarlamanız gerekir.

Uygulamam ve kullandığı cihaz arasındaki bağımlılığı çözmek için bir arayüz uygulamak için doğru bir yaklaşım gibi mi görünüyor?

Basitçe evet

Beton aslında uygulamaya nasıl ulaşır?

Uygulama zamanında beton montaj (dll) yükleyebilirsiniz. Bkz. Https://stackoverflow.com/questions/465488/can-i-load-a-net-assembly-at-runtime-and-instantiate-a-type-knowing-only-the-na

Böyle bir "sürücüyü" dinamik olarak boşaltmanız / yüklemeniz gerekiyorsa, montajı ayrı bir AppDomain'e yüklemeniz gerekir .


Teşekkür ederim, gerçekten kodlamaya ilk başladığımda arayüzler hakkında daha fazla bilgi sahibi olmayı dilerdim.
Snoop

Üzgünüz, bir kısmı eklemeyi unuttum (beton aslında uygulamaya nasıl ulaşır). Buna hitap edebilir misin? Bkz.
Snoop

Aslında uygulamalarınızı DLL'leri çalışma zamanında yükleyerek bu şekilde mi yapıyorsunuz?
Snoop

Örneğin somut sınıf benim tarafımdan bilinmiyorsa, evet. Bir cihaz satıcısının cihazınızı uygulamanızla kullanabilmesi için IDevicearayüzünüzü kullanmaya karar verdiğini varsayalım , o zaman IMO'nun başka bir yolu olmaz.
Heslacher
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.