Организация интерфейсов

Вы, очевидно, используете мьютекс для блокировки данных в одном потоке, к которым одновременно обращается другой поток. Предположим, что вы только что позвонили lock() и в процессе доступа к данным. Это означает, что вы не ожидаете, что какой-либо другой поток (или другой экземпляр того же кода потока) получит доступ к тем же данным, заблокированным тем же мьютексом. То есть, если один и тот же код потока исполняется в другом экземпляре потока, попадает в блокировку, тогда lock() должен блокировать поток управления там. Это относится к потоку, который использует другой код потока, который также обращается к тем же данным и который также заблокирован тем же мьютексом. В этом случае вы все еще находитесь в процессе доступа к данным, и вам может потребоваться, скажем, еще 15 секунд, чтобы достичь разблокировки мьютекса (чтобы другой поток, блокируемый в блокировке мьютекса, разблокировался и позволил элементу управления доступ к данным). Разрешаете ли вы любой ценой позволить еще одному потоку просто разблокировать тот же мьютекс и, в свою очередь, разрешить потоку, который уже ожидает (блокирует) блокировку мьютекса, разблокировать и получить доступ к данным? Надеюсь, вы поняли, что я здесь говорю? В соответствии с согласованным универсальным определением !,

  • с «мьютексом» этого не может быть. Никакой другой поток не может разблокировать блокировку в вашем потоке
  • с помощью «двоичного семафора», это может произойти. Любой другой поток может разблокировать блокировку в вашем потоке

Итак, если вы очень внимательно относитесь к использованию двоичного семафора вместо мьютекса, то вам следует быть очень осторожным в «определении» блокировок и разблокировок. Я имею в виду, что каждый поток управления, который затрагивает каждую блокировку, должен вызывать вызов разблокировки, также не должно быть никакой «первой разблокировки», скорее это должна быть всегда «первая блокировка».

11
задан Aza 11 April 2013 в 06:35
поделиться

4 ответа

Beware of always, ever, and never - especially near all, none, or every.

Should you always put all of you interfaces in a separate assembly? No - not necessarily.

Should you put interfaces that you expect external consumers of your code to implement - possibly. I would put interfaces into an external assembly if you expect multiple assemblies in your project to rely on them - this can help break coupling dependencies. I've also used this practice to solve circular referencing issues, where assemblies need to be aware of interfaces in one another.

I don't put interfaces that are only used internally in a project in a separate assembly. I also don't promote interfaces into their own assembly when my projects are relatively small - or the interfaces are not intended to be used without the assembly that depends on them.

As for the example you presented - I would suggest that you consider NOT referencing classes in your system from interfaces. Whenever possible, I try to have interfaces only reference other interfaces - this keeps things relatively decoupled. You can't always achieve this - and so when you have these types of interfaces - you have to keep them with the assembly they depend on.

If you do decide to put interfaces into a separate assembly - you shouldn't necessarily put them all into a single assembly. You may want to break them out by their intended usage - this way consumers can pull in just the interfaces that are relevant to a particular domain of functionality.

16
ответ дан 3 December 2019 в 06:46
поделиться

I think that cyclic dependancies might be unavoidable even if you declare everything as interfaces, because there might be a method in assembly Foo that takes a parameter IBar, and a method in assembly Bar, that takes a parameter IFoo.

I'd say that there is no universal recipe for this, but you should instead use "common sense" to divide the interfaces in assemblies as needed.

1
ответ дан 3 December 2019 в 06:46
поделиться

What about defining an interface with methods that take generic types? E.g.

public interface IThing
{
    int DoMyThing<T>(T variable);
}

If you need some restrictions on T variable, you could do:

int DoMyThing<T>(T variable) where T : IOtherInterface;

Where IOtherInterface is also defined in your Interfaces project. Then, so long as your particular class inherits from both IThing and IOtherInterface, you can use the DoMyThing method and pass in an instance of your particular class without having a circular dependency. Your code might become:

public interface ICustomButton
{
    void Animate<T>(T strategy) where T : IAnimateable;
}

The interface has no reference to concrete class AnimatorStrategy.

-2
ответ дан 3 December 2019 в 06:46
поделиться

Простой ответ: AnimatorStrategy также должен быть интерфейсом. Реализация AnimatorStrategy находится в проекте Animation, но интерфейс будет в проекте AnimationInterfaces или Interfaces (по мере необходимости).

У меня не обязательно будет один проект интерфейсов, но по одному для каждой функциональной группы. Клиенты Animation будут ссылаться на AnimationInterfaces, а клиенты Gui будут ссылаться на GuiInterfaces.

Таким образом вы сохраните свои контракты, не вмешиваясь в какую-либо реализацию.

Что бы это ни стоило, я предпочитаю пойти другим путем с соглашениями об именах. Поместите интерфейсы в Animation, а реализации в AnimationImpl (или аналогичный). Таким образом, вы ссылаетесь на функциональное имя.

3
ответ дан 3 December 2019 в 06:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: