¿Cómo encapsular variables 'globales' en C #? /mejores prácticas

9

En C #, ¿cuál es la mejor práctica para encapsular variables que necesito usar en varios métodos? ¿Está bien simplemente declararlos en la parte superior de mi clase sobre los dos métodos?

Además, si estoy usando la configuración de la aplicación desde mi archivo de configuración, ¿debería usar un getter? Me gusta esto...

private string mySetting{ get { return WebConfigurationManager.AppSettings["mySetting"]; } }

¿Cuál es la mejor práctica?

usuario1944367
fuente
¿Cuál sería el propósito de un captador, además de agregar una capa adicional (y probablemente innecesaria) de indirección?
Robert Harvey
44
Un captador es mucho mejor que múltiples llamadas WebConfigurationManager.AppSettingsporque es mucho más fácil cambiarlo más tarde
Daniel Little
@Lavinski: Claro, si crees que podrías cambiar el almacén de datos por otro diferente más adelante. En la práctica, eso rara vez ocurre, y la probabilidad de que pueda suceder para AppSettings parece muy pequeña.
Robert Harvey
10
Un "getter" tiene la ventaja de que hace que intellisense funcione, y tiene la cadena de clave "mySetting" (que el compilador no verifica si está escrita correctamente) solo en un lugar.
Doc Brown

Respuestas:

5

No solo está bien. Según el libro Clean Code, en realidad es una muy buena práctica, y el tío Bob realmente lo alienta. Una variable utilizada por muchos métodos podría mostrar un alto grado de cohesión entre los métodos. Además, un alto grado de variables de objeto también podría indicar que dicha clase debería dividirse en dos, por lo que declararlas como variables de objeto podría ayudarlo a encontrar candidatos de clase ocultos.

Las variables de nivel de objeto no son variables globales, así que no tenga miedo de usarlas si se comparten por varios métodos.

Uri
fuente
gracias por tu ayuda, aunque creo que cuando dijiste cohesión realmente quisiste unir.
user1944367
No, quise decir cohesión. En la clase de Ingeniería de Software también tuve dificultades para comprender el deseo de una alta cohesión. Usualmente deseamos bajo acoplamiento y alta cohesión. El acoplamiento es algo físico que podemos ver en nuestros propios métodos. Si una clase usa otra clase, entonces está acoplada a ella. Si realmente crea una instancia y es objeto de dicha clase, entonces es muy parecida. Sin embargo, la cohesión es más una cosa lógica. La alta cohesión en una clase significa que sus métodos pertenecen a un dominio muy similar, incluso si no comparten ninguna variable entre ellos.
Uri
Varios métodos que usan una variable de objeto no necesariamente significan que están acoplados entre sí. Podría tener una clase Encrypter con una variable de contraseña char [] y tener Encrypt (texto de cadena); y descifrar (texto de cadena); métodos dentro de él. Ambos usan la misma variable de contraseña, pero no hay aparente acoplamiento entre ellos. Sin embargo, puede notar que se ocupan del mismo dominio, y es decir, el cifrado de texto. Hasta donde yo sé, tienen un alto grado de cohesión, aunque dicha clase podría dividirse en dos. Se podría argumentar que el cifrado no pertenece al dominio de descifrado.
Uri
4

Encapsular sus configuraciones de manera constante es una gran idea.

Lo que hago es crear una clase de configuración, ya sea una clase global estática o varias clases de instancia que luego administraré con inyección de dependencia. Luego cargo todos los ajustes de configuración en esa clase al inicio.

También he escrito una pequeña biblioteca que hace uso de la reflexión para hacer esto aún más fácil.

Una vez que mi configuración esté en mi archivo de configuración

<?xml version="1.0" encoding="utf-8" ?>
<configuration>   
    <appSettings>
        <add key="Domain" value="example.com" />
        <add key="PagingSize" value="30" />
        <add key="Invalid.C#.Identifier" value="test" />
    </appSettings>
</configuration>

Hago una clase estática o de instancia según mis necesidades. Para aplicaciones simples con solo unas pocas configuraciones, una clase estática está bien.

private static class Settings
{
    public string Domain { get; set; }

    public int PagingSize { get; set; }

    [Named("Invalid.C#.Identifier")]
    public string ICID { get; set; }

}

Luego, utilizando mi llamada a la librería ya sea Inflate.Statico Inflate.Instancey lo bueno es que puedo usar cualquier fuente de valor clave.

using Fire.Configuration;

Inflate.Static( typeof(Settings), x => ConfigurationManager.AppSettings[x] );

Todo el código para esto está en GitHub en https://github.com/Enexure/Enexure.Fire.Configuration

Incluso hay un paquete nuget:

PM> Install-Package Enexure.Fire.Configuration

Código de referencia:

using System;
using System.Linq;
using System.Reflection;
using Fire.Extensions;

namespace Fire.Configuration
{
    public static class Inflate
    {
        public static void Static( Type type, Func<string, string> dictionary )
        {
            Fill( null, type, dictionary );
        }

        public static void Instance( object instance, Func<string, string> dictionary )
        {
            Fill( instance, instance.GetType(), dictionary );
        }


        private static void Fill( object instance, Type type, Func<string, string> dictionary ) 
        {

            PropertyInfo[] properties;
            if (instance == null) {

                // Static
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly );
            } else {

                // Instance
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );
            }

            // Get app settings and convert
            foreach (PropertyInfo property in properties) {
                var attributes = property.GetCustomAttributes( true );
                if (!attributes.Any( x => x is Ignore )) {

                    var named = attributes.FirstOrDefault( x => x is Named ) as Named;

                    var value = dictionary((named != null)? named.Name : property.Name);

                    object result;
                    if (ExtendConversion.ConvertTo(value, property.PropertyType, out result)) {
                        property.SetValue( instance, result, null );
                    }
                }
            }
        }
    }
}
Daniel Little
fuente