Что такое "слабая связь"? Пожалуйста, приведите примеры

Кажется, я не могу понять, что такое "слабая связь". Я полагаю, это не поможет тому, что слово "свободный" обычно имеет отрицательный оттенок, поэтому я всегда забываю, что слабая связь - это хорошо.

Кто-нибудь, пожалуйста, покажите какой-нибудь код "до" и "после" (или псевдокод), который иллюстрирует эту концепцию?

20 ответов

Рассмотрим простое приложение для корзины покупок, которое использует класс CartContents для отслеживания товаров в корзине и класс Order для обработки покупки. Заказ должен определить общую стоимость содержимого в корзине, он может сделать это следующим образом:

Плотно связанный пример:

public class CartEntry
{
    public float Price;
    public int Quantity;
}

public class CartContents
{
    public CartEntry[] items;
}

public class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
        this.cart = cart;
        this.salesTax = salesTax;
    }

    public float OrderTotal()
    {
        float cartTotal = 0;
        for (int i = 0; i < cart.items.Length; i++)
        {
            cartTotal += cart.items[i].Price * cart.items[i].Quantity;
        }
        cartTotal += cartTotal*salesTax;
        return cartTotal;
    }
}

Обратите внимание, как метод OrderTotal (и, следовательно, класс Order) зависит от деталей реализации классов CartContents и CartEntry. Если бы мы попытались изменить эту логику, чтобы учесть скидки, нам, вероятно, пришлось бы изменить все 3 класса. Кроме того, если мы перейдем к использованию коллекции List для отслеживания элементов, нам также придется изменить класс Order.

Теперь вот немного лучший способ сделать то же самое:

Менее связанный пример:

public class CartEntry
{
    public float Price;
    public int Quantity;

    public float GetLineItemTotal()
    {
        return Price * Quantity;
    }
}

public class CartContents
{
    public CartEntry[] items;

    public float GetCartItemsTotal()
    {
        float cartTotal = 0;
        foreach (CartEntry item in items)
        {
            cartTotal += item.GetLineItemTotal();
        }
        return cartTotal;
    }
}

public class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
        this.cart = cart;
        this.salesTax = salesTax;
    }

    public float OrderTotal()
    {
        return cart.GetCartItemsTotal() * (1.0f + salesTax);
    }
}

Логика, характерная для реализации позиции корзины, коллекции корзины или заказа, ограничена только этим классом. Таким образом, мы можем изменить реализацию любого из этих классов без необходимости изменения других классов. Мы могли бы пойти еще дальше, улучшив дизайн, представив интерфейсы и т. Д., Но я думаю, вы понимаете, в чем суть.

Многие интегрированные продукты (особенно Apple), такие как iPod, iPad являются хорошим примером тесной связи: после того, как батарея разрядится, вы также можете купить новое устройство, потому что батарея припаяна и не разряжается, что делает замену очень дорого. Слабосвязанный плеер позволит легко менять батарею.

То же самое касается разработки программного обеспечения: как правило (намного) лучше иметь слабосвязанный код для облегчения расширения и замены (и для облегчения понимания отдельных частей). Но очень редко при особых обстоятельствах может быть выгодно тесное соединение, потому что тесная интеграция нескольких модулей обеспечивает лучшую оптимизацию.

Я буду использовать Java в качестве примера. Допустим, у нас есть класс, который выглядит так:

public class ABC
{
   public void doDiskAccess() {...}
}

Когда я позвоню в класс, мне нужно будет сделать что-то вроде этого:

ABC abc = new ABC();

abc. doDiskAccess();

Все идет нормально. Теперь предположим, что у меня есть другой класс, который выглядит так:

public class XYZ
{
   public void doNetworkAccess() {...}
}

Он выглядит точно так же, как ABC, но, скажем, он работает по сети, а не на диске. Итак, теперь давайте напишем такую ​​программу:

if(config.isNetwork()) new XYZ().doNetworkAccess();
else new ABC().doDiskAccess();

Это работает, но это немного громоздко. Я мог бы упростить это с помощью интерфейса, подобного этому:

public interface Runnable
{
    public void run();
}

public class ABC implements Runnable
{
   public void run() {...}
}

public class XYZ implements Runnable
{
   public void run() {...}
}

Теперь мой код может выглядеть так:

Runnable obj = config.isNetwork() ? new XYZ() : new ABC();

obj.run();

Видите, насколько чище и проще это понять? Мы только что поняли первый основной принцип слабой связи: абстракция. Ключевым моментом здесь является обеспечение того, чтобы ABC и XYZ не зависели от каких-либо методов или переменных классов, которые их вызывают. Это позволяет ABC и XYZ быть полностью независимыми API. Или, другими словами, они "отделены" или "слабо связаны" с родительскими классами.

Но что, если нам нужно общение между ними? Что ж, тогда мы можем использовать дополнительные абстракции, такие как модель событий, чтобы гарантировать, что родительский код никогда не должен соединяться с созданными вами API.

Степень разницы между ответами здесь показывает, почему было бы трудно понять концепцию, но изложить ее так просто, как я могу ее описать:

Чтобы я знал, что если я брошу тебе мяч, то ты сможешь его поймать, мне действительно не нужно знать, сколько тебе лет. Мне не нужно знать, что вы ели на завтрак, и мне действительно все равно, кто был вашей первой страстью. Все, что мне нужно знать, это то, что вы можете поймать. Если я знаю это, то мне все равно, если это ты, я бросаю мяч тебе или твоему брату.

С помощью нединамических языков, таких как C#, Java и т. Д., Мы достигаем этого через интерфейсы. Допустим, у нас есть следующий интерфейс:

public ICatcher
{
   public void Catch();
}

А теперь допустим, что у нас есть следующие классы:

public CatcherA : ICatcher
{
   public void Catch()
   {
      console.writeline("You Caught it");
   }

}
public CatcherB : ICatcher
{
   public void Catch()
   {
      console.writeline("Your brother Caught it");
   }

}

Теперь и CatcherA, и CatcherB реализуют метод Catch, поэтому служба, для которой требуется Catcher, может использовать любой из них и на самом деле не знать, какой это. Таким образом, тесно связанная служба может непосредственно привести к

public CatchService
{
   private CatcherA catcher = new CatcherA();

   public void CatchService()
   {
      catcher.Catch();
   }

}

Таким образом, CatchService может делать именно то, что он намеревался сделать, но он использует CatcherA и всегда будет использовать CatcherA. Он жестко закодирован, поэтому он остается там, пока кто-то не придет и не проведет рефакторинг.

Теперь давайте возьмем другую опцию, которая называется внедрением зависимости:

public CatchService
{
   private ICatcher catcher;

   public void CatchService(ICatcher catcher)
   {
      this.catcher = catcher;
      catcher.Catch();
   }
}

Таким образом, calss, который создает CatchService, может делать следующее:

CatchService catchService = new CatchService(new CatcherA());

или же

CatchService catchService = new CatchService(new CatcherB());

Это означает, что служба Catch не тесно связана ни с CatcherA, ни с CatcherB.

Есть несколько других стратегий для слабого связывания сервисов, таких как использование инфраструктуры IoC и т. Д.

Извините, но "слабая связь" - это не проблема кодирования, это проблема дизайна. Термин "слабая связь" тесно связан с желательным состоянием "высокой когезии", будучи противоположным, но дополняющим.

Слабая связь просто означает, что отдельные элементы дизайна должны быть сконструированы так, чтобы количество ненужной информации, которую они должны знать о других элементах дизайна, было уменьшено.

Высокая когезия - это своего рода "тесная связь", но высокая когезия - это состояние, когда элементы дизайна, которые действительно должны знать друг о друге, спроектированы так, чтобы они работали вместе чисто и элегантно.

Дело в том, что некоторые элементы дизайна должны знать детали о других элементах дизайна, поэтому они должны быть спроектированы таким образом, а не случайно. Другие элементы дизайна не должны знать подробности о других элементах дизайна, поэтому они должны быть разработаны таким образом, целенаправленно, а не случайно.

Реализация этого оставлена ​​в качестве упражнения для читателя:) .

Тесно связанный код опирается на конкретную реализацию. Если мне нужен список строк в моем коде, и я объявляю его следующим образом (на Java)

ArrayList<String> myList = new ArrayList<String>();

тогда я зависим от реализации ArrayList.

Если я хочу изменить это на слабосвязанный код, я делаю свою ссылку интерфейсным (или другим абстрактным) типом.

List<String> myList = new ArrayList<String>();

Это мешает мне вызывать любой метод на myList это специфично для реализации ArrayList. Я ограничен только теми методами, которые определены в интерфейсе List. Если позже я решу, что мне действительно нужен LinkedList, мне нужно изменить свой код только в одном месте, где я создал новый список, а не в 100 местах, где я делал вызовы методов ArrayList.

Конечно, вы можете создать экземпляр ArrayList с помощью первого объявления и не использовать никакие методы, которые не являются частью интерфейса List, но использование второго объявления заставляет компилятор сохранять вашу честность.

Вы можете думать о (жесткой или слабой) связи как о буквальном количестве усилий, которые потребуются вам, чтобы отделить определенный класс от его зависимости от другого класса. Например, если у каждого метода в вашем классе был небольшой блок finally, в котором вы вызывали Log4Net для записи чего-либо, то вы бы сказали, что ваш класс тесно связан с Log4Net. Если бы вместо этого ваш класс содержал закрытый метод с именем LogSomething, который был единственным местом, которое вызывало компонент Log4Net (а вместо этого все другие методы назывались LogSomething), то вы бы сказали, что ваш класс слабо связан с Log4Net (потому что это не займет много усилия, чтобы вытащить Log4Net и заменить его на что-то еще).

Определение

По сути, связь - это то, насколько данный объект или набор объектов полагается на другой объект или другой набор объектов для выполнения своей задачи.

Высокая связь

Придумай машину. Для запуска двигателя ключ должен быть вставлен в зажигание, повернут, должен присутствовать бензин, должна появиться искра, должны сработать поршни и двигатель должен ожить. Можно сказать, что двигатель автомобиля сильно связан с несколькими другими объектами. Это высокая связь, но это не так уж плохо.

Слабая связь

Подумайте о пользовательском элементе управления для веб-страницы, которая отвечает за предоставление пользователям возможности публиковать, редактировать и просматривать некоторые типы информации. Единый элемент управления может использоваться, чтобы позволить пользователю опубликовать новый фрагмент информации или отредактировать новый фрагмент информации. Элемент управления должен иметь возможность совместного использования двух разных путей - новый и редактировать. Если элемент управления написан таким образом, что ему требуются данные определенного типа со страниц, на которых он будет содержаться, вы можете сказать, что он слишком сильно связан. Элемент управления не должен ничего делать со своей страницы контейнера.

Это довольно общая концепция, поэтому примеры кода не дают полной картины.

Один парень здесь, на работе, сказал мне: "Шаблоны похожи на фракталы, их можно увидеть, когда вы приблизитесь очень близко, и когда вы уменьшите масштаб до уровня архитектуры".

Чтение краткой страницы Википедии может дать вам ощущение этой общности:

http://en.wikipedia.org/wiki/Loose_coupling

Что касается конкретного примера кода...

Вот одна слабая связь, с которой я недавно работал, из материала Microsoft.Practices.CompositeUI.

    [ServiceDependency]
    public ICustomizableGridService CustomizableGridService
    {
        protected get { return _customizableGridService; }
        set { _customizableGridService = value; }
    }

Этот код объявляет, что у этого класса есть зависимость от CustomizableGridService. Вместо того, чтобы просто ссылаться на точную реализацию сервиса, он просто заявляет, что требует НЕКОТОРОЙ реализации этого сервиса. Затем во время выполнения система разрешает эту зависимость.

Если это не ясно, вы можете прочитать более подробное объяснение здесь:

http://en.wikipedia.org/wiki/Dependency_injection

Представьте, что ABCCustomizableGridService - это имплементация, которую я собираюсь здесь подключить.

Если я решу, я могу восстановить это и заменить его на XYZCustomizableGridService или StubCustomizableGridService без каких-либо изменений в классе с этой зависимостью.

Если бы я непосредственно ссылался на ABCCustomizableGridService, то мне нужно было бы внести изменения в эти / те ссылки / ы, чтобы перейти в другую реализацию сервиса.

Связывание связано с зависимостями между системами, которые могут быть модулями кода (функциями, файлами или классами), инструментами в конвейере, процессами сервер-клиент и т. Д. Чем менее общие зависимости, тем более "тесно они связаны" они становятся, поскольку для изменения одной системы требуется изменение других систем, которые на нее полагаются. Идеальная ситуация - "слабая связь", когда одна система может быть изменена, и системы, зависящие от нее, будут продолжать работать без изменений.

Общий способ достижения слабой связи - через четко определенные интерфейсы. Если взаимодействие между двумя системами четко определено и соблюдается с обеих сторон, тогда становится легче модифицировать одну систему, гарантируя, что соглашения не нарушаются. На практике часто бывает, что четко определенный интерфейс не установлен, что приводит к небрежной конструкции и жесткой связи.

Некоторые примеры:

  • Приложение зависит от библиотеки. При тесной связи приложение работает на более новых версиях библиотеки. Google для "DLL Ад".

  • Клиентское приложение читает данные с сервера. В условиях жесткой связи изменения на сервере требуют исправлений на стороне клиента.

  • Два класса взаимодействуют в объектно-ориентированной иерархии. При тесной связи изменения одного класса требуют обновления другого класса для соответствия.

  • Несколько инструментов командной строки взаимодействуют в канале. Если они тесно связаны, изменения в версии одного инструмента командной строки вызовут ошибки в инструментах, которые читают его выходные данные.

Связь означает, насколько тесно разные классы связаны друг с другом. Тесно связанные классы содержат большое количество взаимодействий и зависимостей.

Слабосвязанные классы противоположны тем, что их зависимости друг от друга сведены к минимуму и вместо этого полагаются на четко определенные общедоступные интерфейсы друг друга.

Legos, игрушки, которые SNAP вместе сочли бы слабосвязанными, потому что вы можете просто соединить части и построить любую систему, какую захотите. Тем не менее, головоломки имеют кусочки, которые тесно связаны между собой. Вы не можете взять кусок из одной головоломки (системы) и вставить его в другую головоломку, потому что система (головоломка) очень зависит от очень специфических частей, которые были построены специально для этого конкретного "дизайна". Legos построены более общим образом, чтобы их можно было использовать в вашем Lego House или в моем Lego Alien Man.

Ссылка: https://megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/

Рассмотрим приложение для Windows с FormA и FormB. FormA является основной формой, и она отображает FormB. Представьте, что FormB необходимо передать данные своему родителю.

Если вы сделали это:

class FormA 
{
    FormB fb = new FormB( this );

    ...
    fb.Show();
}

class FormB 
{
    FormA parent;

    public FormB( FormA parent )
    {
        this.parent = parent;
    }     
}

FormB тесно связан с FormA. У FormB не может быть другого родителя, кроме родительского типа FormA.

Если, с другой стороны, вы заставили FormB опубликовать событие и подписать FormA на это событие, то FormB может отправить данные обратно через это событие любому подписчику, у которого есть это событие. Тогда в этом случае FormB даже не знает, что говорит с родителем; через слабую связь событие обеспечивает просто общение с подписчиками. Любой тип теперь может быть родительским для FormA.

Р.П.

Два компонента сильно связаны, когда они зависят от конкретной реализации друг друга.

Предположим, у меня есть этот код где-то в методе в моем классе:

this.some_object = new SomeObject();

Теперь мой класс зависит от SomeObject, и они сильно связаны. С другой стороны, допустим, у меня есть метод InjectSomeObject:

void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation
  this.some_object = so;
}

Тогда в первом примере можно просто использовать вставленный SomeObject. Это полезно во время тестирования. При нормальной работе вы можете использовать тяжелые классы, использующие базы данных, сетевые классы и т. Д., В то время как для тестов, проходящих легкую, фиктивную реализацию. С тесно связанным кодом вы не можете этого сделать.

Вы можете сделать некоторые части этой работы более легкими, используя контейнеры для внедрения зависимостей. Вы можете прочитать больше о DI в Википедии: http://en.wikipedia.org/wiki/Dependency_injection.

Иногда легко зайти слишком далеко. В какой-то момент вы должны конкретизировать вещи, иначе ваша программа станет менее читабельной и понятной. Поэтому используйте эту технику в основном на границе компонентов и знайте, что вы делаете. Убедитесь, что вы используете слабую связь. Если нет, то вам, вероятно, не нужно это в этом месте. Я могу сделать вашу программу более сложной. Убедитесь, что вы делаете хороший компромисс. Другими словами, поддерживать хороший баланс. Как всегда при проектировании систем. Удачи!

На простом языке, слабосвязанная означает, что это не зависит от другого события. Это выполняется независимо.

В информатике есть другое значение для "слабой связи", о которой никто больше не писал здесь, так что... Вот и все - надеюсь, вы дадите мне несколько голосов, так что это не потеряно в нижней части кучи! ОБЯЗАТЕЛЬНО, тема моего ответа относится к любому исчерпывающему ответу на вопрос...

Термин "слабая связь" впервые вошел в вычисления как термин, используемый в качестве прилагательного в отношении архитектуры ЦП в конфигурации с несколькими ЦП. Его аналог термин "жесткая связь". Слабая связь - это когда центральные процессоры не разделяют много общих ресурсов, а плотная связь - когда это происходит.

Термин "система" может сбивать с толку, поэтому, пожалуйста, внимательно проанализируйте ситуацию.

Обычно, но не всегда, несколько процессоров в конфигурации оборудования, в которой они существуют в одной системе (как в отдельных блоках "ПК"), будут тесно связаны. За исключением некоторых сверхвысокопроизводительных систем, которые имеют подсистемы, которые на самом деле разделяют основную память между "системами", все делимые системы слабо связаны.

Термины Tightly Coupled и Loosely Coupled были введены до того, как были изобретены многопоточные и многоядерные процессоры, поэтому для этих условий могут понадобиться некоторые компаньоны, чтобы полностью сформулировать ситуацию сегодня. И действительно, сегодня вполне может существовать система, которая объединяет оба типа в одну общую систему. Что касается современных программных систем, то существует две общие архитектуры, по одной на каждый вариант, достаточно общие, они должны быть семейными.

Во-первых, поскольку речь шла о некоторых примерах слабосвязанных систем:

  • VaxClusters
  • Linux-кластеры

Напротив, некоторые примеры из тесно спаренных

  • Semetrical-Multi-Processing (SMP) Операционные системы - например, Fedora 9
  • Многопоточные процессоры
  • Многоядерные процессоры

В современных вычислениях примеры работы в одной общей системе не редкость. Например, возьмем современные двухъядерные или четырехъядерные процессоры Pentium, работающие под управлением Fedora 9 - это тесно связанные между собой вычислительные системы. Затем объедините несколько из них в слабо связанный Linux Cluster, и теперь у вас есть как слабо, так и тесно связанные вычисления! О, разве современное оборудование не прекрасно!

Когда вы создаете объект класса с помощью new ключевое слово в каком-то другом классе, вы на самом деле делаете тесную связь (плохая практика), вместо этого вы должны использовать слабую связь, что является хорошей практикой

--- --- A.java

package interface_package.loose_coupling;

public class A {

void display(InterfaceClass obji)
{
    obji.display();
    System.out.println(obji.getVar());
}
}

--- --- B.java

package interface_package.loose_coupling;

public class B implements InterfaceClass{

private String var="variable Interface";

public String getVar() {
    return var;
}

public void setVar(String var) {
    this.var = var;
}

@Override
public void display() {
    // TODO Auto-generated method stub
    System.out.println("Display Method Called");
}
}

--- --- InterfaceClass

package interface_package.loose_coupling;

public interface InterfaceClass {

void display();
String getVar();
}

--- --- MainClass

package interface_package.loose_coupling;

public class MainClass {

public static void main(String[] args) {
    // TODO Auto-generated method stub

    A obja=new A();
    B objb=new B();
    obja.display(objb);     //Calling display of A class with object of B class 

}
}

Объяснение:

В приведенном выше примере у нас есть два класса A и B

Класс B реализует интерфейс, т.е. InterfaceClass.

InterfaceClass определяет Контракт для класса B, так как InterfaceClass имеет абстрактные методы класса B, к которым может обращаться любой другой класс, например A.

В классе A у нас есть метод отображения, который может исключать объект класса, который реализует InterfaceClass (в нашем случае это класс B). И на этом объекте метод класса A вызывает display() и getVar() класса B

В MainClass мы создали объект классов A и B. И вызвали метод отображения A, передав объект класса B, т.е. objb. Метод отображения A будет вызван с объектом класса B.

Теперь говорим о слабой связи. Предположим, что в будущем вам придется изменить имя класса B на ABC, тогда вам не нужно менять его имя в методе отображения класса B, просто создайте объект new (класс ABC) и передайте его методу отображения в MailClass. Вам не нужно ничего менять в классе А

ссылка: http://p3lang.com/2013/06/loose-coupling-example-using-interface/

Несколько длинных ответов здесь. Принцип очень прост, хотя. Я отправляю вступительное заявление из Википедии:

"Слабая связь" описывает упругие отношения между двумя или более системами или организациями с некоторой разновидностью отношений обмена.

Каждый конец транзакции четко указывает свои требования и делает несколько предположений о другом конце ".

Я предлагаю очень простой тест кодовой связи:

  1. Кусок А кода тесно связан с Куском В кода, если существует какая-либо возможная модификация Куска В, которая приведет к изменениям в Куске А, чтобы сохранить корректность.

  2. Кусок А кода не тесно связан с Куском В кода, если нет никакой возможной модификации Куска В, которая внесла бы необходимые изменения в Кусок А.

Это поможет вам проверить степень связи между частями вашего кода. для рассуждений об этом см. этот пост в блоге: http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/

Вы можете прочитать больше об общей концепции "слабой связи".

Короче говоря, это описание отношений между двумя классами, где каждый класс знает как минимум о другом, и каждый класс мог бы продолжать нормально работать, независимо от того, присутствует другой или нет, и без зависимости от конкретной реализации другого. учебный класс.

Возможно, лучшая метафора - это брак.

Когда вы не состоите в браке, вы плохо связаны.

Вы можете оставить своего партнера легче.

Когда вы "женаты", вы тесно связаны.

Например, в некоторых странах вы должны платить алименты, когда уходите от партнера.

Слабая связь, как правило, это 2 актера, работающих независимо друг от друга на одной рабочей нагрузке Таким образом, если бы у вас было 2 веб-сервера, использующих одну и ту же внутреннюю базу данных, вы бы сказали, что эти веб-серверы слабо связаны. Примером тесной связи может служить наличие двух процессоров на одном веб-сервере... эти процессоры тесно связаны.

Надеюсь, это несколько полезно.

Другие вопросы по тегам