【问题标题】:nested classes and interfaces嵌套类和接口
【发布时间】:2018-04-19 21:50:39
【问题描述】:

(如果有人想帮助解决这个问题,我真的很难为这个问题想出一个好的标题......)

所以我在设计某些东西时遇到了问题。本质上,我有一个 A 类,它由 B 类型的对象数组组成。我只希望 A 类的接口被公开,并且希望 B 类对任何用户基本上都是隐藏的。我希望能够对类型 B 及其数据执行操作,但只能通过类 A 的接口/方法调用 B 实例的方法。棘手的部分是我想创建一个对成员执行操作的方法类型 B,但我想实现一个接口,然后有一个实现该接口的类,因为我希望我的用户能够创建他们自己的此方法的实现。我正在考虑做类似的事情:

public class A 
{
    B[] arr;
    C c;

    public A(C c) 
    { 
        arr = new B[100];
        this.c = c;
    }


    public void method1() 
    {
        var b = new B();
        b.someMethodofb(c);    // pass c to the method of b
    }

    private class B 
    {
        someMethodOfb(C c) 
        {
        }
    }
}

public class C : Interface1 
{
    public void method(B b) 
    {    
        //interface method we have implemented
    }
}

我将 B 类设为私有,因为我只希望 A 类公开可用,因此 B 类发生的任何事情都通过 A 类发生,这也是我将 B 嵌套在 A 中的原因。但是由于 B 类是私有的,我会能够将它用作我的 C 类方法的参数吗? Interface1实现的方法会影响B如何执行someMethodOfb的内部实现,这就是为什么我认为我需要将它传入才能保持B类的隐藏性质。有没有更好的方法让我设计这个并能够实现我在​​第一段中设定的目标?

【问题讨论】:

  • 所以您希望B 是私有的,但您还想要一个将method(B b) 公开给全世界的接口?这两个要求有冲突。
  • 您是否遇到 X/Y 问题? meta.stackexchange.com/questions/66377/what-is-the-xy-problem .... 你能解释一下用例吗?也许你需要一种不同的方法?
  • @Charleh 它与工作有关,我不确定我能提供多少细节。本质上,我有一个类 A,其中包含一个 B 类型的数组。我还想要一个对 B 类型执行操作的方法,但我需要能够让用户实现他们自己版本的该方法。我想我可以使该方法成为 B 类型的方法,但我想限制 B 类型在世界上的暴露程度。
  • 不是在接口中公开类型 B,您需要公开一个公开可接受的 API,您可以通过您控制的类型 A 的方法将其转换为类型 B。
  • 短广告 =)。我自己实现了嵌套类通用场景的解决方案,这是answer

标签: c# interface inner-classes


【解决方案1】:

我建议您为 B 的公共已知端添加另一个接口,让 B 实现该接口并让 C 的方法使用该接口。

public interface IC {
    void method(IB b);
}

public interface IB {
    int Priority { get; set; }
    int Urgency { get; set; }
}

public class A {
    B[] arr;
    IC c;

    public A(C c) {
        arr = new B[100];
        this.c = c;
    }


    public void method1() {
        var r = (new Random()).Next(100);
        arr[r].someMethodOfB(c);    // pass c to the method of b
    }

    private class B : IB {
        public int Priority { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
        public int Urgency { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        internal void someMethodOfB(IC aC) {
            aC.method(this);
            throw new NotImplementedException();
        }
    }
}

public class C : IC { // user implements
    public void method(IB b) {
        if (b.Priority > 10 || b.Urgency > 10)
            ; // do something with BI using b
        throw new NotImplementedException();
    }
}

现在类的用户需要知道IC,这样他们才能创建C,他们需要知道IB,这样他们才能在C中编写方法的主体,但他们不需要了解所有B 或访问B

【讨论】:

  • @IanMercer 正确的命名留给读者作为练习(尽管我确实更改为前缀为I)。
  • 我还强烈建议您考虑将 B[] arr 更改为 IB[]。这些类耦合得越少,你就越容易为 A 编写测试用例。A 不应该关心 B 是如何实现的,只关心它具有预期的属性和方法。与 Fake B 合作应该同样愉快。
  • @AdamG 我不同意。关键是IBB 的公共接口,但A 可以访问B 的私有端——否则,就没有私有端,有什么意义?
  • @NetMage,关键是你的类应该依赖于抽象而不是具体。这包括 A。在测试 A 的行为时,您可能需要注入一些虚假的 B 对象,这些对象会导致 A 中的行为。具有自己内部验证/行为的复杂类型会使此类极端情况很难测试。这对于像 dto 这样的简单 B 类来说不太重要。我应该在这里做个区分。您公开发布的 IB 可能与您的类在数组定义中使用的接口不同且具有限制性,从而允许您的类访问第三方无法看到的方法。
  • @AdamG IString 在哪里?如果您为项目中的每个类编写一个界面,我可以提出一个可以提高您的工作效率的建议。
【解决方案2】:

让我们用具体的例子:)

假设我们有三个类:Customer、Order 和 OrderProcessor。 Customer 和 Order 是分别代表一个客户和一个订单的实体,而 OrderProcessor 将处理一个订单:

public interface IOrderProcessor
{
    void ProcessOrder(IOrder order);
}

public interface IOrder
{
    void FinalizeSelf(IOrderProcessor oProc);
    int CustomerId {get; set;}
}

public class Customer
{
    List<IOrder> _orders;
    IOrderProcessor _oProc;
    int _id;

    public Customer(IOrderProcessor oProc, int CustId)
    {
        _oProc = oProc;
        _orders = new List<IOrder>();
        _id = CustId;
    }

    public void CreateNewOrder()
    {
        IOrder _order = new Order() { CustomerId = _id };
        _order.FinalizeSelf(_oProc);
        _orders.Add(_order);
    }

    private class Order : IOrder
    {
        public int CustomerId {get; set;}
        public void FinalizeSelf(IOrderProcessor oProcessor)
        {
            oProcessor.ProcessOrder(this);
        }
    }
}
public class ConcreteProcessor : IOrderProcessor
{
    public void ProcessOrder(IOrder order)
    {
        //Do something
    }
}

【讨论】:

    猜你喜欢
    • 2011-08-01
    • 2012-09-02
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-26
    • 1970-01-01
    相关资源
    最近更新 更多