¿Cómo administrar las personalizaciones del código fuente para muchos usuarios?

7

Cuando escribí el código original, estaba usando TortoiseSVN para administrar versiones, pero luego surgió un nuevo problema. Los clientes deseaban la personalización en pocas cosas y ahora la lista de clientes está creciendo. Quiero mantener el control de versiones dependiendo de los clientes.

Quiero mantener un código fuente raíz y luego agregar cambios de versión dependiendo de los clientes. ¿Hay alguna forma simple?

    
pregunta RPK 31.07.2011 - 07:03

3 respuestas

9

Puede mantener sucursales separadas para cada cliente y fusionar los cambios de su troncal, como dbb está diciendo. Sin embargo, hay una buena manera de hacerlo y una mala manera en la que esto se complica y se le escapa rápidamente si hay muchas funciones similares compartidas entre varios clientes.

La forma más fácil de manejar variantes, para cada cliente , es a través de ramificación por abstracción en lugar. En pocas palabras: cree una configuración de manejo en su aplicación / sistema que active y desactive la funcionalidad que necesitan sus clientes. De esa manera, no necesita preocuparse por la fusión a través del control de código fuente. Puede ser tan complejo como un sistema de plug-in, pero también puede hacerlo simplemente teniendo una función en su programa que pase por un archivo de configuración y habilite la funcionalidad que necesita.

La forma de crear esto depende de en qué entorno lo estés desarrollando. Java tiene una clase de Propiedades que puede hacer esto por usted. En C # puede consultar aquí para obtener más información. Aunque también podría codificar estas propiedades con una caja de conmutación o un mapa hash, y administrar las diferencias de esta clase de configuración a través del control de origen, en caso de que no desee que sus clientes reconfiguren fácilmente el software.

Ejemplos en Java:

Lee un archivo de configuración con java.util.Properties

Fuente:

public class ReadConfig {
    private static final CONFIG_FILEPATH = "config.txt";

    private Properties config;

    public ReadConfig() {
        config = new Properties();
        config.load(CONFIG_FILEPATH);
    }

    public boolean isEnabled(String functionality) {
        String s = config.getProperty(functionality);
        return Boolean.getBoolean(s);
    }
}

Contenido de config.txt (que se puede administrar para cada cliente):

Eat_Sandwich: true
Make_Sandwich: false

Ejemplo de uso:

public static void main(String[] args) {
    ReadConfig myConfig = new ReadConfig();
    if(myConfig.isEnabled("Eat_Sandwich")) {
        System.out.println("I can eat the sandwich");
    }
    if(myConfig.isEnabled("Make_Sandwich")) {
        System.out.println("I can make the sandwich");
    }
}
// Will output: I can eat the sandwich

Codifique la configuración

Fuente (que se puede administrar para cada cliente):

public class HardCodedConfig {
    private HashMap<String, Boolean> config;

    public HardCodedConfig() {
        config = new HashMap<String, Boolean>();

        // Add functionalities here:
        register("Make_Sandwich", true);
        register("Eat_Sandwich", false);
    }

    private void register(String functionality, boolean enabled) {
        config.put(functionality, enabled);
    }

    private boolean isEnabled(String functionality) {
        return config.get(functionality);
    }
}

Uso:

public static void main(String[] args) {
    HardCodedConfig myConfig = new HardCodedConfig();
    if(myConfig.isEnabled("Eat_Sandwich")) {
        System.out.println("I can eat the sandwich");
    }
    if(myConfig.isEnabled("Make_Sandwich")) {
        System.out.println("I can make the sandwich");
    }
}
// Will output: I can make the sandwich

EDITAR:

Dado que el OP quiere algo que es un poco más avanzado que habilitar funciones, también puede hacerlo cargando bibliotecas. Pero debe tener cuidado al agregar bibliotecas externas (como dll's y jar's) en el repositorio de origen, ya que eso le daría trabajo adicional de limpieza de la casa. Entonces, si tiene algún archivo que se pueda generar a través de scripts de compilación (sí usa un servidor de compilación , ¿verdad?), Entonces no lo haga. incluirlos en el control de código fuente.

En su lugar, realice un seguimiento de las diferentes dlls que necesita para compilar y usar a través de scripts de construcción configurables, para que pueda construir su aplicación desde cero junto con instaladores para cada cliente.

También considere usar patrones como el Strategy Pattern , para separar diferentes implementaciones de la misma funcionalidad. En su ejemplo, el cálculo de descuentos se puede hacer creando una interfaz y / o una clase abstracta. Aquí hay una implementación simple en C #:

public interface IDiscountStrategy {

    /**
     * Calculates the discount from amount
     */
    public decimal calculateDiscount(decimal amount);

}

public class DefaultDiscountStrategy : IDiscountStrategy {

    public decimal _percentage;

    public DefaultDiscountStrategy(decimal percentage) {
        _percentage = percentage;
    }

    public decimal calculateDiscount(decimal amount) {
        return amount * _percentage;
    }

}

El uso es llamar al método calculateDiscount en la estrategia de descuento que está cargada.

En su dll de biblioteca separada para un cliente específico, tiene las siguientes opciones que se utilizan cuando se carga la aplicación:

public class WeirdCustomerDiscountStrategy : IDiscountStrategy {

    public decimal calculateDiscount(decimal amount) {
        DayOfWeek dayOfWeek = DateTime.Now.DayOfWeek;
        if (dayOfWeek == DayOfWeek.Thursday)
            return amount * 0.05;
        else
            return 0;
    }

}

En tu aplicación común, cargarás las diferentes estrategias como esta:

public IDiscountStrategy getDiscountStrategy() {
    Assembly assembly;
    try {
        assembly = Assembly.LoadFrom("CustomerXYZ.dll");
    } catch (FileNotFoundException e) {
        assembly = null;
    }
    IDiscountStrategy discounter;
    if (assembly == null) {
        discounter = new DefaultDiscountStrategy(0.10);
    } else { 
        discounter = (IDiscountStrategy) 
            assembly.CreateInstance("WeirdCustomerDiscountStrategy");
    }
    return discounter;
}

Esto se vuelve un poco peludo cuando la aplicación crece. Por lo tanto, es posible que desee considerar usar un marco IoC para hacer esto por usted, como StructureMap o autofaq si está utilizando .NET, o Spring si están utilizando Java. Aquí hay un ejemplo de de un "escáner de complementos" en StructureMap .

    
respondido por el Spoike 31.07.2011 - 09:15
1

En lugar de intentar que SCM resuelva este problema por usted, asegúrese de que el script de compilación pueda crear versiones separadas del producto. Cómo exactamente haces eso depende de los lenguajes de programación; para algo como PHP o Python, es posible que incluso tenga que escribir un script de copia de archivos poco sofisticado que coloque los archivos correctos en los lugares correctos antes de la implementación. En cualquier caso, la idea es que todas las versiones se puedan derivar del mismo código base, y seleccionar / crear / implementar una es una tarea totalmente automatizada.

    
respondido por el tdammers 31.07.2011 - 17:52
0

Puede mantener una sucursal separada para cada cliente y luego fusionar los cambios desde su " tronco "según sea necesario.

    
respondido por el dbb 31.07.2011 - 07:37

Lea otras preguntas en las etiquetas