Sử dụng giao diện cho mã kết nối lỏng lẻo


10

Lý lịch

Tôi có một dự án phụ thuộc vào việc sử dụng một loại thiết bị phần cứng nhất định, trong khi thực sự không quan trọng ai là người tạo ra thiết bị phần cứng đó miễn là nó làm những gì tôi cần. Với điều đó đã được nói, thậm chí hai thiết bị được cho là làm điều tương tự sẽ có sự khác biệt khi chúng không được sản xuất bởi cùng một nhà sản xuất. Vì vậy, tôi đang suy nghĩ sử dụng một giao diện để tách ứng dụng khỏi kiểu dáng / kiểu thiết bị cụ thể có liên quan và thay vào đó, giao diện chỉ bao gồm các chức năng cấp cao nhất. Đây là những gì tôi nghĩ kiến ​​trúc của tôi sẽ như thế nào:

  1. Xác định một giao diện trong một dự án C # IDevice.
  2. Có một cụ thể trong một thư viện được xác định trong một dự án C # khác, sẽ được sử dụng để đại diện cho thiết bị.
  3. Có thiết bị cụ thể thực hiện IDevicegiao diện.
  4. Các IDevicegiao diện có thể có các phương pháp như GetMeasurementhay SetRange.
  5. Làm cho ứng dụng có kiến ​​thức về bê tông và truyền cụ thể vào mã ứng dụng sử dụng ( không thực hiện ) IDevicethiết bị.

Tôi khá chắc chắn rằng đây là cách phù hợp để thực hiện, bởi vì sau đó tôi sẽ có thể thay đổi thiết bị nào đang được sử dụng mà không ảnh hưởng đến ứng dụng (đôi khi dường như xảy ra). Nói cách khác, việc triển khai GetMeasurementhoặc SetRangethực sự hoạt động thông qua cụ thể như thế nào (có thể khác nhau giữa các nhà sản xuất thiết bị).

Nghi ngờ duy nhất trong đầu tôi là bây giờ cả ứng dụng và lớp cụ thể của thiết bị đều phụ thuộc vào thư viện chứa IDevicegiao diện. Nhưng, đó có phải là một điều xấu?

Tôi cũng không thấy ứng dụng sẽ không cần biết về thiết bị, trừ khi thiết bị và IDeviceở trong cùng một không gian tên.

Câu hỏi

Điều này có vẻ như là cách tiếp cận phù hợp để thực hiện giao diện để tách rời sự phụ thuộc giữa ứng dụng của tôi và thiết bị mà nó sử dụng?


Đây hoàn toàn là cách chính xác để làm điều đó. Về cơ bản, bạn đang lập trình trình điều khiển thiết bị và đây chính xác là cách trình điều khiển thiết bị được viết theo truyền thống, với lý do chính đáng. Bạn không thể hiểu được rằng để sử dụng các khả năng của thiết bị, bạn phải dựa vào mã biết các khả năng này ít nhất là theo một cách trừu tượng.
Kilian Foth

@KilianFoth Vâng, tôi có cảm giác, quên thêm một phần vào câu hỏi của mình. Xem # 5.
Snoop

Câu trả lời:


5

Tôi nghĩ rằng bạn đã hiểu khá rõ về cách thức hoạt động của phần mềm tách rời :)

Nghi ngờ duy nhất trong đầu tôi là bây giờ cả ứng dụng và lớp cụ thể của thiết bị đều phụ thuộc vào thư viện chứa giao diện IDevice. Nhưng, đó có phải là một điều xấu?

Nó không cần phải như vậy!

Tôi cũng không thấy ứng dụng sẽ không cần biết về thiết bị như thế nào, trừ khi thiết bị và IDevice ở trong cùng một không gian tên.

Bạn có thể giải quyết tất cả những mối quan tâm này với Cấu trúc dự án .

Cách tôi thường làm:

  • Đặt tất cả những thứ trừu tượng của tôi trong một Commondự án. Một cái gì đó như MyBiz.Project.Common. Các dự án khác là miễn phí để tham khảo nó, nhưng nó có thể không tham khảo các dự án khác.
  • Khi tôi tạo ra một triển khai cụ thể của một bản tóm tắt, tôi đặt nó vào một dự án riêng. Một cái gì đó như MyBiz.Project.Devices.TemperatureSensors. Dự án này sẽ tham khảo Commondự án.
  • Sau đó tôi có Clientdự án của tôi đó là điểm khởi đầu cho ứng dụng của tôi (đại loại như MyBiz.Project.Desktop). Khi khởi động, ứng dụng trải qua quá trình Bootstrapping nơi tôi định cấu hình ánh xạ trừu tượng hóa / triển khai cụ thể. Tôi có thể khởi tạo cụ thể của mình IDevicesnhư thế nào WaterTemperatureSensorIRCameraTemperatureSensorở đây, hoặc tôi có thể định cấu hình các dịch vụ như Factories hoặc IoC container để khởi tạo các loại bê tông phù hợp cho tôi sau này.

Điều quan trọng ở đây là chỉ có Clientdự án của bạn cần nhận thức được cả Commondự án trừu tượng và tất cả các dự án triển khai cụ thể. Bằng cách giới hạn bản đồ trừu tượng-> cụ thể vào mã Bootstrap của bạn, bạn có thể làm cho phần còn lại của ứng dụng của bạn không biết về các loại cụ thể.

Mã ghép lỏng lẻo ftw :)


2
DI làm theo tự nhiên từ đây. Đó cũng chủ yếu là một mối quan tâm về cấu trúc dự án / mã. Có một bộ chứa IoC có thể giúp với DI nhưng đó không phải là điều kiện tiên quyết. Bạn cũng có thể đạt được DI bằng cách tiêm phụ thuộc thủ công!
MetaFight

1
@StevieV Vâng, nếu đối tượng Foo trong ứng dụng của bạn yêu cầu IDevice, việc đảo ngược phụ thuộc có thể đơn giản như việc tiêm nó thông qua một hàm tạo ( new Foo(new DeviceA());), thay vì có một trường riêng trong chính Foo khởi tạo DeviceA ( private IDevice idevice = new DeviceA();) - bạn vẫn đạt được DI, như Foo không biết về DeviceA trong trường hợp đầu tiên
một

2
@anotherdave của bạn và đầu vào MetaFight rất hữu ích.
Snoop

1
@StevieV Khi bạn có thời gian, đây là một đoạn giới thiệu hay về Nghịch đảo phụ thuộc như một khái niệm (từ "Chú Bob" Martin, anh chàng đã tạo ra nó), khác biệt với khung Inversion of Control / Depency Injection, như Spring (hoặc một số thứ khác ví dụ phổ biến trong thế giới C♯ :))
một

1
@anotherdave Chắc chắn có kế hoạch để kiểm tra xem, cảm ơn bạn một lần nữa.
Snoop

3

Vâng, đó có vẻ như là cách tiếp cận đúng. Không, nó không phải là một điều xấu cho ứng dụng và thư viện thiết bị phụ thuộc vào giao diện, đặc biệt nếu bạn đang kiểm soát việc thực hiện.

Nếu bạn lo ngại rằng các thiết bị có thể không phải lúc nào cũng thực hiện giao diện, vì bất kỳ lý do gì, bạn có thể sử dụng mẫu bộ điều hợp và điều chỉnh việc triển khai cụ thể của thiết bị với giao diện.

BIÊN TẬP

Khi giải quyết mối quan tâm thứ năm của bạn, hãy nghĩ đến một cấu trúc như thế này (tôi giả sử bạn đang kiểm soát việc xác định thiết bị của mình):

Bạn có một thư viện cốt lõi. Trong đó là một giao diện gọi là IDevice.

Trong thư viện thiết bị của bạn, bạn có một tham chiếu đến thư viện lõi của mình và bạn đã xác định một loạt các thiết bị mà tất cả đều thực hiện IDevice. Bạn cũng có một nhà máy biết cách tạo ra các loại IDevice khác nhau.

Trong ứng dụng của bạn, bạn bao gồm các tham chiếu đến thư viện lõi và thư viện thiết bị. Ứng dụng của bạn hiện sử dụng nhà máy để tạo các phiên bản của các đối tượng phù hợp với giao diện IDevice.

Đây là một trong nhiều cách có thể để giải quyết mối quan tâm của bạn.

VÍ DỤ:

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.
            }
        }
    }
}

Vậy với việc thực hiện này, bê tông đi đâu?
Snoop

Tôi chỉ có thể nói cho những gì tôi sẽ chọn làm. Nếu bạn kiểm soát các thiết bị, bạn vẫn sẽ đặt các thiết bị cụ thể vào thư viện của riêng họ. Nếu bạn không kiểm soát các thiết bị, bạn sẽ tạo một thư viện khác cho các bộ điều hợp.
Giá Jones

Tôi đoán điều duy nhất là, làm thế nào để ứng dụng có quyền truy cập vào cụ thể mà tôi cần?
Snoop

Một giải pháp sẽ là sử dụng mô hình nhà máy trừu tượng để tạo IDevices.
Giá Jones

1

Nghi ngờ duy nhất trong đầu tôi là bây giờ cả ứng dụng và lớp cụ thể của thiết bị đều phụ thuộc vào thư viện chứa giao diện IDevice. Nhưng, đó có phải là một điều xấu?

Bạn cần phải nghĩ cách khác. Nếu bạn không đi theo cách này, ứng dụng sẽ cần biết về tất cả các thiết bị / triển khai khác nhau. Điều bạn cần lưu ý là, việc thay đổi giao diện đó có thể phá vỡ ứng dụng của bạn nếu nó đã bị loại bỏ, do đó bạn nên thiết kế giao diện đó một cách cẩn thận.

Điều này có vẻ như là cách tiếp cận phù hợp để thực hiện giao diện để tách rời sự phụ thuộc giữa ứng dụng của tôi và thiết bị mà nó sử dụng?

Đơn giản là có

làm thế nào cụ thể thực sự đến ứng dụng

Ứng dụng có thể tải lắp ráp bê tông (dll) trong thời gian chạy. Xem: /programming/465488/can-i-load-a-net-assinstall-at-r nb-and-factstant-a-type -knowing-only-the-na

Nếu bạn cần tự động tải / tải một "trình điều khiển" như vậy, bạn cần tải cụm vào một AppDomain riêng .


Cảm ơn bạn, tôi thực sự muốn tôi biết nhiều hơn về giao diện khi tôi mới bắt đầu viết mã.
Snoop

Xin lỗi, quên thêm vào một phần (cách cụ thể thực sự vào ứng dụng). Bạn có thể giải quyết điều đó? Xem # 5.
Snoop

Bạn có thực sự làm các ứng dụng của bạn theo cách này, tải DLL vào thời gian chạy không?
Snoop

Nếu ví dụ lớp bê tông không được tôi biết đến thì có. Giả sử nhà cung cấp thiết bị quyết định sử dụng IDevicegiao diện của bạn để thiết bị của anh ta có thể được sử dụng với ứng dụng của bạn, sau đó sẽ không có cách nào khác IMO.
Heslacher
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.