(Мне очень трудно было придумать хорошее название для этого вопроса, если кто-то хочет помочь с этим ..)
Так что у меня проблема с дизайном чего-то. По сути, у меня есть класс 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. спроектировать это и достичь целей, которые я изложил в первом абзаце?
Вы столкнулись с проблемой X / Y? meta.stackexchange.com/questions/66377/what-is-the-xy-proble м .... Вы можете объяснить вариант использования - может быть, вам нужен другой подход?
@Charleh, это связано с работой, и я не уверен, сколько подробностей я могу дать. По сути, у меня есть класс A, содержащий массив типа B. Мне также нужен метод, который выполняет операции с типом B, но мне нужно позволить пользователю реализовать свою собственную версию этого метода. Думаю, я мог бы сделать этот метод методом типа B, но я хотел ограничить, насколько уязвимым является мир типа B.
Вместо того, чтобы предоставлять тип B в интерфейсе, вам необходимо предоставить общедоступный API, который вы можете перенести на тип B в методе типа A, который вы контролируете.
@FrostyStraw, некоторое время назад у меня было почти такое же требование, взгляните на этот вопрос, это действительно мне очень помогло
Короткое объявление =). Я реализовал решение для сценария, в котором ваш вложенный класс является общим, это отвечать
В основном у вас есть общедоступный вложенный класс, но с частным конструктором =)
@pmcilreavy а что, если я сделаю только конструктор B закрытым (пока класс останется открытым)? Может ли метод (B b) по-прежнему быть представлен миру?
@NetMage Я не уверен, что понимаю, что вы имеете в виду. Вы могли бы привести какие-нибудь примеры?
@taquion, спасибо, это выглядит многообещающе, я посмотрю, смогу ли я сделать с ним то, что мне нужно
Не уверен, что это поможет: возможно, B реализует интерфейс, содержащий someMethodOfB (c). У вас все еще может быть B как вложенный класс, но C.method () принимает интерфейс B вместо конкретного B.





Я бы посоветовал вам добавить еще один интерфейс для публично известной стороны 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 Я не согласен. Дело в том, что IB является публичным интерфейсом для B, но что A имеет доступ к частной стороне B - в противном случае частной стороны нет, и в чем смысл?
@NetMage, дело в том, что ваши классы должны зависеть от абстракций, а не от конкреций. Это включает A. При тестировании поведения A вам может потребоваться ввести некоторые поддельные объекты B, которые вызывают поведение в A. Сложный тип со своими собственными внутренними проверками / поведениями может сделать такие угловые случаи очень трудными для тестирования. Это менее важно для простого класса B, такого как dto. Я должен здесь провести различие. IB, который вы публикуете публично, может отличаться от интерфейса, используемого вашим классом в определении массива, и ограничивать его, позволяя вашему классу получить доступ к методам, которые не могут видеть третьи стороны.
@AdamG Где IString? Если вы напишете интерфейс для каждого класса в своих проектах, я могу сделать предложение, которое повысит вашу продуктивность.
Reductio ad adurdum. Мы не говорим о строках, иначе OP не нуждался бы в интерфейсе. B, очевидно, ведет себя нежелательно разоблачать. Строки неизменны. Меня не волнует, использовали ли они string.Join, чтобы создать тот, который они передали. Рассмотрим класс ProductDispatcher. При тестировании вы хотите иметь дело с созданием конкретного объекта клиента и добавлением счетов за просрочку? Затем вам нужны настоящие продукты, производители и т. д. Ваши тесты ProductDispatcher терпят неудачу каждый раз, когда меняются другие классы. Нет, вам просто нужно, чтобы ICustomer ответил InArrears. Это улучшает мою продуктивность.
Возьмем конкретные примеры :)
Скажем, у нас есть три класса: 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
}
}
Итак, вы хотите, чтобы
Bбыл приватным, но вам также нужен интерфейс, открывающий доступ кmethod(B b)миру? Эти 2 требования противоречат друг другу.