¿Cuál es la diferencia entre acoplamiento flojo y acoplamiento estrecho en el paradigma orientado a objetos?


Respuestas:


338

El acoplamiento apretado es cuando un grupo de clases depende mucho el uno del otro.

Este escenario surge cuando una clase asume demasiadas responsabilidades, o cuando una preocupación se extiende a muchas clases en lugar de tener su propia clase.

El acoplamiento flojo se logra mediante un diseño que promueve la responsabilidad individual y la separación de preocupaciones.

Una clase débilmente acoplada puede consumirse y probarse independientemente de otras clases (concretas).

Las interfaces son una herramienta poderosa para usar para desacoplar. Las clases pueden comunicarse a través de interfaces en lugar de otras clases concretas, y cualquier clase puede estar en el otro extremo de esa comunicación simplemente implementando la interfaz.

Ejemplo de acoplamiento apretado:

class CustomerRepository
{
    private readonly Database database;

    public CustomerRepository(Database database)
    {
        this.database = database;
    }

    public void Add(string CustomerName)
    {
        database.AddRow("Customer", CustomerName);
    }
}

class Database
{
    public void AddRow(string Table, string Value)
    {
    }
}

Ejemplo de acoplamiento flojo:

class CustomerRepository
{
    private readonly IDatabase database;

    public CustomerRepository(IDatabase database)
    {
        this.database = database;
    }

    public void Add(string CustomerName)
    {
        database.AddRow("Customer", CustomerName);
    }
}

interface IDatabase
{
    void AddRow(string Table, string Value);
}

class Database implements IDatabase
{
    public void AddRow(string Table, string Value)
    {
    }
}

Otro ejemplo aquí .


Hasta ahora, lo que dijo tiene mucho sentido ... ¿Puede explicar cómo el mecanismo de acoplamiento puede relacionarse con el patrón de Observador?
Jim

1
El patrón de observación se describe aquí: en.wikipedia.org/wiki/Observer_pattern . Dado que la clase Asunto puede mantener una lista de clases que heredan de 'Observador', sin saber realmente el tipo concreto de esas clases, esta es una instancia de acoplamiento flexible. El Sujeto no depende de ninguno de sus Observadores o sus preocupaciones internas. Los observadores no dependen del sujeto ni de ninguna de sus preocupaciones.
Jonathan

44
Las interfaces Java son herramientas que pueden ayudarlo. Sin embargo, no son obligatorios. El concepto de programa para la interfaz, no la implementación, significa programa para los métodos / atributos públicos (o aquellos que están destinados a ser utilizados por personas que llaman externas cuando los idiomas no admiten modificadores de acceso, como C). Se supone que la interfaz cumple con este contrato implícito al no cambiar. En lenguajes sin modificadores de acceso (como C), significa solo usar interfaces / funciones publicadas y no tocar aquellas que están destinadas a usarse internamente, ya que pueden cambiar según sea necesario para admitir las funciones publicadas.
Bill Rosmus

3
@ Jonathan Jonathan, señor, pero ambos códigos hacen lo mismo: ¿cuál es la diferencia entre ellos? es decir, ¿qué ventajas hay para acoplar libremente?
BKSpurgeon

1
He visto (básicamente) este ejemplo antes. En mi opinión, eso no importaría si hubiera muchos tipos de bases de datos que se iban a utilizar. Si planea tener solo uno, entonces, ¿es malo usar una implementación directamente, luego, si va a necesitar inyectar diferentes tipos, luego refactorice el uso único de la base de datos para usar una interfaz? ¿En lugar de agregar cargas de código de placa de caldera que probablemente no sea necesario?
Carlos Bribiescas

178

Explicación sin CUALQUIER código

Ejemplo de resumen:

El sombrero está "débilmente acoplado" al cuerpo. Esto significa que puede quitarse fácilmente el sombrero sin hacer ningún cambio en la persona / cuerpo. Cuando puedes hacer eso, entonces tienes "acoplamiento flojo". Ver abajo para más detalles.

El sombrero está "débilmente acoplado" al cuerpo.  Esto significa que puedes quitarte fácilmente el sombrero sin hacer ningún cambio en la persona / cuerpo.  Atribución de imagen: https://pixabay.com/en/greeting-cylinder-chapeau-dignity-317250/

Acoplamiento apretado (ejemplo detallado)

Piensa en tu piel. Está pegado a tu cuerpo. Se ajusta como un guante. Pero, ¿y si quisieras cambiar el color de tu piel de blanco a verde? ¿Te imaginas lo doloroso que sería despegar tu piel, teñirla y luego pegarla de nuevo, etc.? Cambiar su piel es difícil porque está estrechamente acoplado a su cuerpo. Simplemente no puedes hacer cambios fácilmente. Tendría que rediseñar fundamentalmente a un ser humano para que esto sea posible.

  • Punto clave n. ° 1 : en otras palabras, si desea cambiar la piel, también DEBERÍA cambiar el diseño de su cuerpo porque los dos están unidos, están estrechamente unidos.

Dios no era un buen programador orientado a objetos.

Acoplamiento suelto (ejemplo detallado)

Ahora piensa en vestirte por la mañana. ¿No te gusta el azul? No hay problemas: puedes ponerte una camisa roja. Puede hacerlo fácilmente y sin esfuerzo porque la camisa no está realmente conectada a su cuerpo de la misma manera que su piel. La camisa no sabe ni le importa de qué cuerpo se trata . En otras palabras, puedes cambiarte de ropa, sin cambiar realmente tu cuerpo.

  • Ese es el punto clave # 2. Si cambia su camisa, entonces no está obligado a cambiar su cuerpo ; cuando puede hacerlo, entonces tiene un acoplamiento flojo. Cuando no puedes hacer eso, entonces tienes un acoplamiento apretado.

Ese es el concepto básico en pocas palabras.

¿Por qué es todo esto importante?

Es importante porque el software cambia todo el tiempo. En términos generales, desea poder modificar fácilmente su código, sin cambiar su código. Sé que suena como un oxímoron, pero por favor tengan paciencia conmigo.

Ejemplos prácticos de acoplamiento en la codificación

  • Ejemplos de CSV / JSON / DB: si alguien quiere su salida en un archivo CSV en lugar de JSON, etc., o si desea cambiar de MySQL a PostGreSQL, debería poder hacer esos cambios extremadamente fácilmente en su código, sin tener que volver a escribir toda la clase, etc. En otras palabras, no desea acoplar estrechamente su aplicación con una implementación de base de datos específica (por ejemplo, Mysql) o con una salida particular (por ejemplo, archivos CSV). Porque, como es inevitable en el software, vendrán cambios. Cuando llegan, es mucho más fácil si sus partes de su código están unidas libremente.

  • Ejemplo de piezas de automóviles: si alguien quiere su automóvil en negro , no debería tener que rediseñar todo el automóvil para hacerlo. Un automóvil y sus repuestos serían un ejemplo perfecto de una arquitectura débilmente acoplada. Si desea reemplazar su motor por uno mejor, debería poder simplemente quitar su motor sin demasiado esfuerzo y cambiarlo por uno mejor. Si su automóvil solo funciona con motores Rolls Royce 1234 y ningún otro motor, entonces su automóvil estará estrechamente acoplado a ese motor (Rolls Royce 1234). Sería mejor si cambiara el diseño de su automóvil para que funcione con cualquiermotor, para que esté un poco más flojo junto con sus componentes. ¡Aún mejor sería si su automóvil pudiera funcionar sin necesidad de un motor! Va a ocurrir una cierta cantidad de acoplamiento, pero debe trabajar para minimizarlo tanto como sea posible. ¿Por qué? Porque cuando los requisitos cambian, aún deberíamos poder entregar software de buena calidad, muy rápidamente y nos ayuda en ese objetivo mediante un acoplamiento flexible.

Resumen

En resumen, el acoplamiento suelto hace que el código sea más fácil de cambiar. Las respuestas anteriores proporcionan un código que vale la pena leer en este momento.

Polimorfismo y principios sólidos

Re: @TimoHuovinen comenta: el concepto de acoplamiento suelto va de la mano con los conceptos de polimorfismo. Si comprende la analogía básica de una camisa / autopartes, entonces estará listo para darle sentido al polimorfismo. La mejor manera, en este punto, es leer las muestras de código proporcionadas por mis estimados colegas en las otras respuestas en este hilo. Si digo algo más, puede que te sobrecargues con demasiada información.

Atribución de imagen .


8
Esta es una explicación subestimada para los nuevos programadores. Es difícil pasar por las grandes palabras que otros publicamos, una vez que comprendes los conceptos básicos, es más fácil ir a las grandes palabras jajaja
usuario2763557

66
Hay algunas cosas que deben estar estrechamente acopladas y otras unidas libremente a su entorno. El uso de la piel no es una analogía adecuada para un acoplamiento apretado. Si se piensa que la piel está estrechamente unida al cuerpo, también lo es cualquier otra parte. El cuerpo (como un todo) tiene que tener partes (estrechamente integradas) para funcionar correctamente (puede ser como lo pensó la naturaleza, un arquitecto brillante). Si esas partes fueron diseñadas para ser reemplazables (tan simple como cambiar un sombrero), entonces el significado mismo del cuerpo 'humano' pierde su definición. Comentario 1/2.
lupchiazoem

66
Siguiendo el ejemplo, si la piel está hecha para ser reemplazable, entonces la cabeza también debe ser reemplazable de nuevo. Si eso sucede, entonces la gente podría no ser reconocible de una reunión a otra. Una buena analogía de acoplamiento apretado / suelto sería: un automóvil y sus partes, una computadora y sus partes, etc. Si hay un problema con el mouse / teclado de una computadora, se puede reemplazar con otra parte en lugar de inutilizar toda la computadora y tirar a la basura. Comentario 2/2.
lupchiazoem

1
@BKSpurgeon ¡Explicación increíble! y su forma de tomar un ejemplo para la explicación también es realmente buena.
Kiran Joshi

55
Explicado muy creativamente. Estoy estrechamente vinculado a esta respuesta.
AliN11

72

En el diseño orientado a objetos, la cantidad de acoplamiento se refiere a cuánto depende el diseño de una clase del diseño de otra clase. En otras palabras, ¿con qué frecuencia los cambios en la clase A fuerzan los cambios relacionados en la clase B? El acoplamiento apretado significa que las dos clases a menudo cambian juntas, el acoplamiento flojo significa que son en su mayoría independientes. En general, se recomienda un acoplamiento flojo porque es más fácil de probar y mantener.

Puede encontrar útil este documento de Martin Fowler (PDF) .


"¿con qué frecuencia los cambios en la clase A fuerzan los cambios relacionados en la clase B?" Necesito un breve ejemplo para la oración anterior?
Kumaresan Perumal

15

En general, el acoplamiento apretado es malo, pero la mayoría de las veces, porque reduce la flexibilidad y la reutilización del código, hace que los cambios sean mucho más difíciles, impide la capacidad de prueba, etc.

El objeto estrechamente acoplado es un objeto que necesita saber bastante el uno del otro y, por lo general, depende mucho de las interfaces entre sí. Cambiar un objeto en una aplicación estrechamente acoplada a menudo requiere cambios en varios otros objetos. En una aplicación pequeña, podemos identificar fácilmente los cambios y hay menos posibilidades de perder algo. Pero en las aplicaciones grandes, no todos los programadores conocen siempre estas interdependencias o existe la posibilidad de perder los cambios. Pero cada conjunto de objetos débilmente acoplados no depende de otros.

En resumen, podemos decir que el acoplamiento flexible es un objetivo de diseño que busca reducir las interdependencias entre los componentes de un sistema con el objetivo de reducir el riesgo de que los cambios en un componente requieran cambios en cualquier otro componente. El acoplamiento flexible es un concepto mucho más genérico destinado a aumentar la flexibilidad de un sistema, hacerlo más fácil de mantener y hacer que todo el marco sea más 'estable'.

El acoplamiento se refiere al grado de conocimiento directo que un elemento tiene de otro. podemos decir, por ejemplo: A y B, solo B cambia su comportamiento solo cuando A cambia su comportamiento. Un sistema débilmente acoplado puede descomponerse fácilmente en elementos definibles.


11

Cuando dos objetos están débilmente acoplados, pueden interactuar pero tienen muy poco conocimiento el uno del otro.

Los diseños poco acoplados nos permiten construir sistemas OO flexibles que pueden manejar el cambio.

El patrón de diseño del observador es un buen ejemplo para hacer que las clases se acoplen libremente, puedes echarle un vistazo en Wikipedia .


6

Un extracto de mi publicación de blog sobre el acoplamiento:

¿Qué es el acoplamiento apretado ?

Como en la definición anterior, un objeto estrechamente acoplado es un objeto que necesita saber acerca de otros objetos y generalmente depende en gran medida de las interfaces de cada uno.

Cuando cambiamos un objeto en una aplicación estrechamente acoplada, a menudo requiere cambios en varios otros objetos. No hay problema en una pequeña aplicación, podemos identificar fácilmente el cambio. Pero en el caso de aplicaciones grandes, estas interdependencias no siempre son conocidas por todos los consumidores u otros desarrolladores o hay muchas posibilidades de cambios futuros.

Tomemos un código de demostración del carrito de compras para comprender el acoplamiento estrecho:

namespace DNSLooseCoupling
{
    public class ShoppingCart
    {
        public float Price;
        public int Quantity;

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

    public class ShoppingCartContents
    {
        public ShoppingCart[] items;

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

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

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

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

Problemas con el ejemplo anterior

El acoplamiento apretado crea algunas dificultades.

Aquí, los OrderTotal()métodos nos dan la cantidad completa de los artículos actuales de los carros. Si queremos agregar las características de descuento en este sistema de carrito. Es muy difícil de hacer en el código anterior porque tenemos que hacer cambios en cada clase ya que está muy estrechamente acoplado.


6

El acoplamiento flojo significa que el grado de dependencia entre dos componentes es muy bajo.
Ejemplo: SIM GSM

El acoplamiento apretado significa que el grado de dependencia entre dos componentes es muy alto.
Ejemplo: CDMA Mobile


5

Según tengo entendido, la arquitectura estrechamente acoplada no proporciona mucha flexibilidad para el cambio en comparación con la arquitectura débilmente acoplada.

Pero en el caso de arquitecturas poco acopladas, formatos de mensajes o plataformas operativas o la renovación de la lógica empresarial no afecta al otro extremo. Si el sistema se retira para una renovación, por supuesto, el otro extremo no podrá acceder al servicio por un tiempo, pero aparte de eso, el extremo sin cambios puede reanudar el intercambio de mensajes como era antes de la renovación.


5

Acoplamiento ajustado significa que una clase depende de otra clase.
Bajo acoplamiento significa que una clase depende de la interfaz en lugar de la clase.

En el acoplamiento estrecho , hay una dependencia codificada declarada en los métodos.
En acoplamiento flojo , debemos pasar la dependencia externamente en tiempo de ejecución en lugar de codificada. (Los sistemas de pareja suelta usan la interfaz para disminuir la dependencia con la clase).

Por ejemplo, tenemos un sistema que puede enviar resultados de dos o más formas, como salida JSON, salida CSV, etc.

Apretado acoplado

public interface OutputGenerator {
    public void generateOutput();
}

public class CSVOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("CSV Output Generator");
    }
}

public class JSONOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("JSON Output Generator");
    }
}

// In Other Code, we write Output Generator like...
public class Class1 {
    public void generateOutput() {
        // Here Output will be in CSV-Format, because of hard-coded code.
        // This method tightly coupled with CSVOutputGenerator class, if we want another Output, we must change this method.
        // Any method, that calls Class1's generateOutput will return CSVOutput, because Class1 is tight couple with CSVOutputGenerator.
        OutputGenerator outputGenerator = new CSVOutputGenerator();
        output.generateOutput();
    }
}

En el ejemplo anterior, si queremos cambiar la salida en JSON, entonces necesitamos encontrar y cambiar todo el código, porque Class1 está estrechamente acoplado con la clase CSVOutputGenerator.

Suelto acoplado

public interface OutputGenerator {
    public void generateOutput();
}

public class CSVOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("CSV Output Generator");
    }
}

public class JSONOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("JSON Output Generator");
    }
}

// In Other Code, we write Output Generator like...
public class Class1 {
    public void generateOutput(OutputGenerator outputGenerator) {
        // if you want to write JSON, pass object of JSONOutputGenerator (Dependency will be passed externally to this method)
        // if you want to write CSV, pass object of CSVOutputGenerator (Dependency will be passed externally to this method)

        // Due to loose couple with class, we don't need to change code of Class1, because Class1 is loose coupled with CSVOutputGenerator or JSONOutputGenerator class
        // Any method, that calls Class1's generateOutput will desired output, because Class1 does not tight couple with CSVOutputGenerator or JSONOutputGenerator class
        OutputGenerator outputGenerator = outputGenerator;
        output.generateOutput();
    }
}

3

Existen ciertas herramientas que proporcionan inyección de dependencia a través de su biblioteca, por ejemplo, en .net tenemos ninject Library .

Si va más allá en Java, entonces la primavera proporciona estas capacidades.

Los objetos poco acoplados pueden hacerse introduciendo interfaces en su código, eso es lo que hacen estas fuentes.

Di en tu código que estás escribiendo

Myclass m = new Myclass();

ahora esta declaración en su método dice que usted depende de myclassesto se llama estrechamente acoplado. Ahora proporciona una inyección de constructor, o una inyección de propiedad y un objeto de instanciación, entonces se acoplará libremente.


3

Aquí hay muchas respuestas agradables usando analogías, pero un amigo en el trabajo me dio un ejemplo que me gustó más que todos los mencionados aquí ... ¡Ojos y gafas!

Acoplamiento apretado

Un acoplamiento apretado serían los ojos. Si quiero arreglar mi visión, es muy costoso recibir un trasplante de ojo y conlleva un riesgo considerable. Pero, ¿qué pasaría si el diseñador (siendo la raza humana) encontrara una mejor manera? ¡Agregue una función que esté acoplada sin apretar al cuerpo para que se pueda cambiar fácilmente! (si .. gafas)

Bajo acoplamiento

Puedo reemplazar mis lentes fácilmente sin romper mi visión subyacente. Puedo quitarme las gafas y mi visión será como era antes (ni mejor ni peor). El uso de diferentes pares de anteojos cambia la forma en que vemos el mundo a través de nuestros ojos con poco riesgo y fácil mantenimiento.

Resumen

Entonces, la próxima vez que alguien le pregunte "¿a quién le importa si mi código está estrechamente acoplado?" La respuesta tiene que ver con el esfuerzo por cambiar, el esfuerzo por mantener y el riesgo de cambio.

Entonces, ¿cómo se hace esto en C #? ¡Interfaces e inyección de dependencias!

EDITAR

Este también es un buen ejemplo del patrón Decorador, donde los ojos son la clase que estamos decorando al cumplir con los requisitos de la interfaz, pero brindando una funcionalidad diferente (por ejemplo, gafas de sol, gafas de lectura, lupas para joyeros, etc.)


2

El acoplamiento flojo es una respuesta a las dependencias codificadas de estilo antiguo y problemas relacionados, como la recompilación frecuente cuando algo cambia y la reutilización del código. Hace hincapié en implementar la lógica del trabajador en los componentes y evitar el código de conexión específico de la solución allí.

Acoplamiento suelto = IoC Vea esto para una explicación más fácil.


3
No creo que el acoplamiento flojo sea lo mismo que la inversión de control. La inversión del control es una técnica muy útil para reducir el acoplamiento de su diseño, pero existen muchas otras técnicas.
Don Kirkby

2

El acoplamiento flojo es el proceso de proporcionar la dependencia que su clase necesita indirectamente sin proporcionar toda la información de la dependencia (es decir, desde la interfaz) en caso de que el acoplamiento estrecho que proporcione directamente en la dependencia no sea una buena forma de codificación.


2

Se trata de la tasa de dependencia de clases a otras que es tan baja en acoplamientos sueltos y tan alta en acoplamientos ajustados. Para que quede claro en la arquitectura de orientación del servicio , los servicios se acoplan libremente entre sí contra el monolítico, qué clases de dependencia entre sí es a propósito


1

Si la creación / existencia de un objeto depende de otro objeto que no se puede adaptar, es un acoplamiento estrecho. Y, si la dependencia se puede adaptar, su acoplamiento flojo. Considere un ejemplo en Java:

class Car {

    private Engine engine = new Engine( "X_COMPANY" ); // this car is being created with "X_COMPANY" engine
    // Other parts

    public Car() { 
        // implemenation 
    }

}

El cliente de Carclase puede crear uno con SOLO motor "X_COMPANY".

Considere romper este acoplamiento con la capacidad de cambiar eso:

class Car {

    private Engine engine;
    // Other members

    public Car( Engine engine ) { // this car can be created with any Engine type
        this.engine = engine;
    }

}

Ahora, a Carno depende de un motor de "X_COMPANY", ya que se puede crear con tipos.

Una nota específica de Java: el uso de interfaces Java solo para desacoplar no es un enfoque de diseño adecuado. En Java, una interfaz tiene un propósito: actuar como un contrato que proporciona intrínsecamente un comportamiento / ventaja de desacoplamiento.

El comentario de Bill Rosmus en respuesta aceptada tiene una buena explicación.


0

El acoplamiento apretado significa que las clases y los objetos dependen unos de otros. En general, el acoplamiento estrecho generalmente no es bueno porque reduce la flexibilidad y la reutilización del código, mientras que el acoplamiento flojo significa reducir las dependencias de una clase que usa la clase diferente directamente.

Acoplamiento ajustado El objeto estrechamente acoplado es un objeto que necesita saber acerca de otros objetos y generalmente depende en gran medida de las interfaces de cada uno. Cambiar un objeto en una aplicación estrechamente acoplada a menudo requiere cambios en varios otros objetos. En las aplicaciones pequeñas, podemos identificar fácilmente los cambios y hay menos posibilidades de perder algo. Pero en aplicaciones grandes, estas interdependencias no siempre son conocidas por todos los programadores y existe la posibilidad de pasar por alto los cambios. Ejemplo:

    class A {
       public int a = 0;
       public int getA() {
          System.out.println("getA() method");
          return a;
       }
       public void setA(int aa) {
          if(!(aa > 10))
             a = aa;
       }
    }
    public class B {
       public static void main(String[] args) {
          A aObject = new A();
          aObject.a = 100; // Not suppose to happen as defined by class A, this causes tight coupling.
          System.out.println("aObject.a value is: " + aObject.a);
       }
    }

In the above example, the code that is defined by this kind of implementation uses tight coupling and is very bad since class B knows about the detail of class A, if class A changes the variable 'a' to private then class B breaks, also class A's implementation states that variable 'a' should not be more than 10 but as we can see there is no way to enforce such a rule as we can go directly to the variable and change its state to whatever value we decide.

    Output
    aObject.a value is: 100

Loose Coupling
Loose coupling is a design goal to reduce the inter-dependencies between components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.
Loose coupling is a much more generic concept intended to increase the flexibility of the system, make it more maintainable and makes the entire framework more stable.
Example:

class A {
   private int a = 0;
   public int getA() {
      System.out.println("getA() method");
      return a;
   }
   public void setA(int aa) {
      if(!(aa > 10))
         a = aa;
   }
}
public class B {
   public static void main(String[] args) {
      A aObject = new A();
      aObject.setA(100); // No way to set 'a' to such value as this method call will
                         // fail due to its enforced rule.
      System.out.println("aObject value is: " + aObject.getA());
   }
}

En el ejemplo anterior, el código que se define mediante este tipo de implementación utiliza un acoplamiento flexible y se recomienda ya que la clase B tiene que pasar por la clase A para obtener su estado donde se aplican las reglas. Si la clase A se cambia internamente, la clase B no se romperá, ya que usa solo la clase A como una forma de comunicación.

Output
getA() method
aObject value is: 0
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.