Ocultar e encapsulación en C ++
Todos escoitamos sobre a encapsulación da información nas linguas Orientado a obxectos e en C ++. Vexamos aquí o que consiste en e algúns “trucos” que podemos facer no caso específico de C ++ e que normalmente non aparecen nos libros desta lingua (aínda que en libros en patróns de deseño).
Os puntos que veremos son:
- Ataque dos trampladores nunha clase
- Importancia de Lanegegnalesulational para compilar en C ++
- encapsulamento a través das interfaces
encapsulamento dos atributos dunha clase
Primeiro de todo, debería estar claro que a encapsulação, como calquera bo O hábito de programación (como non poñer, comentar, etc.) é útil para o código que posteriormente pode ser reutilizado ou modificado por outras persoas ou por si mesmo. Se fago un programa marciano e nunca non teño que tocar de novo, non importa o que fago con Gouts e sen comentar mentres me atopo mentres o estou facendo e funciona. Vou pagar este “pecado” se dentro de dous meses podo pensar en melloralo ou quero reutilizar algo do seu código para outro programa.
comento isto porque a encapsulación, levou ao seu final, como é o caso do punto final das interfaces, fai que a programación sexa un pouco máis complicada (ten que facer máis clases). Este esforzo só é recompensado se o código é moi grande (evitando recomprilatos innecesarios) ou será reutilizado no futuro (podemos extraer clases con menos dependencias doutras clases). Dito isto, imos ao tema.
Calquera curso de orientación de obxectos dinos que é mellor poñer os atributos dunha clase protexida ou privada (nunca pública) e acceder a eles a través de métodos públicos que poñemos na clase. Vexamos a razón. Supoña que, por exemplo, que solicite un programa que lle permita traer unha lista de persoas coas datas de nacemento. Entre outras cousas, decidimos facernos a nosa clase de data con varios métodos marabillosos do seguinte xeito.
Int Anho; // o anho con catro figuras, por exemplo. 2004
Int mes; // o mes, de 1 a 12
día int; // o día, de 1 a 31
metodomaravilloso1 ();
Metodomaravillos de Vichos2 ();
};
Xa fixemos a clase. Agora facemos o resto do código e nalgunhas miles de miles de código que usamos cousas como esta directamente.
unaccha.anho = 2004;
unaccha.mes = 1;
unaccha.dia = 25;
Finalmente rematamos o noso programa e todo funciona moi ben. Poucos días despois dinos que o programa vai manter tropecías de miles de persoas e que levan moitos arquivos que ver se podemos facer algo para remedio. Guau, almacenamos unha cita con tres enteiros. Se usamos o formato da maioría das computadoras, no que a data é o número de segundos desde o 1 de xaneiro de 1970 (que nos devolve a función ()), basta con un número enteiro.
Total, que mans cambian, cambiou a nosa clase para que teña o seguinte:
/ * comentado por porinefectial
Intanho; Intmes;
Intendencia; * /
Longnumersegunds;
VOIDEMETODOMARAVILLOSO1 ();
Metodomaravillos de Vichos2 ();
};
xa está feito o sinxelo. Agora só tes que pasar polas tropas de miles de código cambiando as nosas tarefas e lecturas aos tres enteiros anteriores polo novo tempo.
sería moito mellor se fixésemos estes tres enteiros e métodos protexidos para acceder a eles. Algo parecido a esta
Void Tomafecha (Int Anho, Int mes, Intdia);
int dameanho ();
Int Damemes ();
int damedia ();
Void Metgetomaravilloso1 ();
Metodomaravillos de Vichos2 ();
Protexido:
Int Anho; // o anho con catro figuras, por exemplo. 2004
Int mes; // o mes, de 1 a 12
día int; // O día, de 1 a 31
};
Se agora temos que facer o mesmo cambio, só cambia os atributos protexidos. Os métodos TomaxXX () e DAMEXXX () son mantidos en termos de parámetros e valor devolto, pero o seu código interno modifícase para facer o ano, o mes e o día nun longo segundo e ao revés. O resto do código non ten que tocalo en absoluto.
É aínda mellor facer os atributos privados que están protexidos. Facelos protexidos, as clases de fillas (aqueles que herdan da data) poden acceder directamente a estes atributos. Cando facemos o cambio por moito tempo, tamén debemos cambiar o código das clases de fillas. Se os atributos son privados e forzamos as clases de fillas para acceder a eles a través de métodos, tampouco teremos que cambiar o código destas clases de fillas.
Acceso a través de métodos é menos eficiente que facelo directamente, polo que, aínda que despois do principio de ocultación é mellor facer atributos privados, por eficiencia nalgúns casos, quizais sexa mellor facelos protexidos (ou mesmo públicos ) Risco de ter que cambiar máis liñas de código en caso de cambio.
sempre que sexa posible os atributos dunha clase privada.
Importancia da encapsulación en C ++
co que se conta ata agora evitamos Ten que cambiar de código en caso de parámetros cambiantes.
No caso de formigón de C ++ hai un pequeno problema adicional. É bastante normal facer que as clases se definan mediante dous ficheiros. No caso da clase de data teriamos unha data.h coa definición da clase e unha data.cc (ou .cpp) co código dos métodos de clase. Cando queremos usar a clase de data, normalmente facemos a nosa #include < data.h >.
Calquera proceso de compilación eficiente (como Linux facer utilidade e eu creo que o visual C ++) está listo o suficiente para recompilar só aqueles ficheiros que deben ser recompilados. É dicir, se xa temos o noso proxecto compilado e xogamos un ficheiro, o compilador só compilará ese ficheiro e todos aqueles que dependen del. Esta característica é moi importante en grandes proxectos (con moitos ficheiros e moitas liñas de código), para gardar o tempo de compilación cada vez que realizamos unha modificación (traballei en proxectos que me levou a compilar desde cero ao redor de 4 horas).
Cal é o problema? O problema é que, se decidimos, por exemplo, cambiar de novo o atributo privado da clase de data para outra cousa, necesitamos tocar o ficheiro data.h. Isto fará que todos os ficheiros fagan #include < data.h > e todos os ficheiros que fan #include desde algún ficheiro que á súa vez fai #Include data. Ben, así por diante.
A solución é evidente, coloque o mínimo posible na data. H, específicamente as variables #definas e globais que non son necesarias para ver desde outras clases.
Por exemplo, a nosa clase de data podería ter algún #define para indicar cal é o número mínimo e máximo de mes. É mellor colocar estes #define na data. CC en vez de data. H, a menos que alguén teña que velos.
#define mes_minimo 1
#define mes_maximo 12
sempre que sexa posible, poñendo O # define, tipos de definición, constantes globais, etc, dentro delfichero.cc
encapsulation a través de interfaces
que quedamos unha cousa que queda. Por que temos que recompilar moitas cousas se cambiamos un atributo privado da clase? O ideal sería poder cambiar as cousas internas da clase sen ter que recompilar nada máis, despois de todo, o atributo é privado e ninguén o usa directamente.
É bastante común na programación orientada a obxectos o uso de interfaces para que as clases dependen entre si. No caso de C ++, o uso de interfaces tamén é útil para evitar recompilacións innecesarias.
Unha interface non é máis que unha clase na que se definen os métodos públicos necesarios, pero non están implementados. A continuación, a clase de formigón que queremos facer herdar esa interface e implementar os seus métodos.
No noso caso, podemos facer unha clase de interface, con métodos públicos virtuais puros (sen código). A continuación, a clase de data herda da interface e implementa eses métodos.
No ficheiro de interface. Haberiamos
{
Público:
Virtual Vidual Tomaccha (Int Anho, Intmes, Int Dia) = 0; Virtual int dameanho () = 0;
Damemes Int virtuales () = 0;
virtual int damedia () = 0; Metodomaravilloso1 () = 0; Void Virtual Metodomaravillos2 () = 0;
};
Polo momento, nin sequera existiría unha interface. CC
A clase de data é aínda a mesma, pero herdada da interface.
Data de clase: Interface pública
{
Público:
Void Tomafa (Int Anho, Int Mes, Intia);
int dameanho ();
Int Damemes ();
int damedia ();
Void Metgetomaravilloso1 ();
Metodomaravillos de Vichos2 ();
Protexido:
Int Anho; // o anho con catro figuras, por exemplo. 2004
Int mes; // o mes, de 1 a 12
día int; // O día, de 1 a 31
};
Agora, todos os que precisan unha data, ten que ter un punteiro para a interface en vez de ata a data. Alguén instalará a data e mantéñase nese punteiro. É dicir, poderiamos facer algo así como este
#include < interface. >
…Interface infectada * Unacy = null;
…
Unacho = Nova data ();
unaccha- > Tomacca (2004, 1, 27);
…
Eliminar unacio;
unaccha = null;
Se miramos un pouco, aínda non fixamos nada, a menos que complica o asunto. O que fai que este código ten que facer agora #include tanto a interfacefea.h como a data.h. Se tocamos algo na data. H, este código será recompido.
Este código necesita #include < data.h > para poder facer a data da data. Ten que buscar como evitar ese novo. Normalmente tamén é bastante habitual facer unha clase (ou usar a mesma interface se o idioma permítelle, como é o caso de C ++) para poñer un método estático que o novo e devolvelo a nós.
No caso de Java, poñendo este método, xa non teriamos unha interface, senón unha clase. Facer información informada herdada limita connosco non herdar de nada (Java non admite a herdanza múltiple). Se isto é admisible, podemos facelo así. Se necesitamos esa data herdada doutra clase, no canto de poñer o método estático na interface, debemos facer un terceiro xerador por separado con este método estático.
No noso exemplo de C ++, quedaría a clase de interface.
{
Público:
Staticinterfacefacea * Damenuevafecha ();
Voidtomacy virtual (Intanho, intmes, día int) = 0; Virtual int dameanho () = 0;
Damemes Int virtuales () = 0;
virtual int damedia () = 0; Metodomaravilloso1 () = 0; Void Virtual Metodomaravillos2 () = 0;
};
Agora necesitamos unha interface. CC. Dentro del teremos
data.
}
O código que utilizou anteriormente o punteiro para a interface sería agora
…
interface * unaccha = null;
…
Unaccha = Interface infectado :: Damenuecha ();
unaccha- > Tomacca (2004, 1, 27);
…
Eliminar unacio;
Unaccha = null;
Como vemos, só o #include da interfacefeape.h e iso non inclúe a data.h (interface. CC, non o .h). Fixemos que este código non vexa en toda a data. Agora podemos xogar sen mirar a data. H, que este código non ten que ser recompilado.
Unha vantaxe adicional é que pode cambiar a data de data para outra clase Data2 en tempo de execución. Sería suficiente poñer un atributo estático na interface para indicar que data de clase que queremos e facer o método de Damenueveevacha () instantie e devolver un ou outro dependendo do atributo.
Usar interfaces paraquelas Clasque WEEP que pode cambiar Del Droyectoo de Develoo De Desenvolver a orde.
Este mecanismo recibe unha instancia dunha clase a través dun método estático e Unha interface, para non depender da clase específica, creo que dentro do mundo dos patróns de deseño é o patrón de fábrica.