Come gestire le personalizzazioni del codice sorgente per molti utenti?

7

Quando ho scritto il codice originale, stavo usando TortoiseSVN per gestire le versioni, ma in seguito un nuovo problema si è evoluto. I clienti desideravano la personalizzazione su poche cose e ora l'elenco dei clienti è in crescita. Voglio mantenere il controllo della versione in base ai clienti.

Voglio mantenere un codice sorgente root e aggiungere modifiche alla versione a seconda dei clienti. C'è un modo semplice?

    
posta RPK 31.07.2011 - 07:03
fonte

3 risposte

9

Potresti mantenere filiali separate per ogni cliente e unire le modifiche dal tuo trunk, proprio come dbb sta dicendo. Tuttavia, esiste un buon modo per farlo e un modo sbagliato in cui tutto ciò diventa complicato e rapidamente fuori controllo se esistono molte funzionalità simili condivise tra più clienti.

Il modo più semplice per gestire le varianti, ciascuna per ogni cliente , è attraverso ramificazione per astrazione anziché. In poche parole: crea la gestione della configurazione nell'applicazione / sistema che attiva e disattiva la funzionalità richiesta dai clienti. In questo modo non devi preoccuparti di unire il controllo del codice sorgente. Può essere complesso come un sistema plug-in, ma puoi farlo semplicemente avendo una funzione nel tuo programma che passa attraverso un file di configurazione e abilita la funzionalità di cui hai bisogno.

Come costruirlo dipende da quale ambiente lo stai sviluppando. Java ha una classe Proprietà che può farlo per te. In C # puoi cercare qui per maggiori informazioni. Sebbene sia possibile codificare in modo rigido queste proprietà con una switch-case o una mappa hash e gestire le differenze di questa classe di configurazione tramite il controllo del codice sorgente, nel caso in cui non si desideri che i clienti riconfigurino facilmente il software.

Esempi in Java:

Leggi un file di configurazione con java.util.Properties

Fonte:

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);
    }
}

Contenuto di config.txt (che può essere gestito per ogni cliente):

Eat_Sandwich: true
Make_Sandwich: false

Esempio di utilizzo:

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

Codice hard della configurazione

Origine (che può essere gestita per ogni 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);
    }
}

Utilizzo:

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

EDIT:

Dato che l'OP vuole qualcosa di un po 'più avanzato rispetto all'attivazione delle funzionalità, è possibile farlo attraverso il caricamento delle librerie. Ma dovresti fare attenzione ad aggiungere librerie esterne (come dll e jar) nel repository di origine, dato che ti farebbe un lavoro extra di pulizia della casa da fare. Quindi se hai file che possono essere generati tramite script di compilazione (usi un build server , giusto?), Allora non includile nel controllo del codice sorgente.

Invece, tieni traccia delle diverse DLL che devi compilare e utilizzare tramite gli script di configurazione configurabili, in modo da poter creare l'applicazione da zero insieme ai programmi di installazione per ciascun cliente.

Considera anche l'utilizzo di modelli come il Pattern di strategia , per separare diverse implementazioni della stessa funzionalità. Nel tuo esempio, calcolare gli sconti, può essere fatto creando un'interfaccia e / o una classe astratta. Ecco una semplice implementazione in 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;
    }

}

L'utilizzo è chiamare il metodo calculateDiscount su DiscountStrategy che è stato caricato.

Nella tua libreria dll separata per un cliente specifico hai il seguente che viene usato invece quando l'applicazione carica:

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;
    }

}

Nella tua applicazione comune caricherete le diverse strategie in questo modo:

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;
}

Diventa piuttosto peloso quando l'applicazione cresce. Quindi potresti prendere in considerazione l'idea di utilizzare un framework IoC per farlo, ad esempio StructureMap o autofaq se utilizzi .NET o Spring se stanno usando Java. Ecco un esempio di "plug-in scanner" in StructureMap .

    
risposta data 31.07.2011 - 09:15
fonte
1

Invece di provare a far sì che un SCM risolva questo problema, fai in modo che lo script di compilazione possa creare versioni separate del prodotto. Come esattamente ciò che fai dipende dai linguaggi di programmazione; per qualcosa come PHP o Python, potresti persino dover scrivere un piccolo script di copia di file sofisticato che mette i file giusti nei posti giusti prima della distribuzione. In ogni caso, l'idea è che tutte le versioni possano essere derivate dalla stessa base di codice, e selezionare / costruire / distribuire una è un'attività completamente automatizzata.

    
risposta data 31.07.2011 - 17:52
fonte
0

Potresti mantenere una filiale separata per ogni cliente e quindi unire le modifiche dal tuo " tronco "se necessario.

    
risposta data 31.07.2011 - 07:37
fonte

Leggi altre domande sui tag