tag:blogger.com,1999:blog-15537002829346631382024-03-08T15:22:49.061+01:00The Art of the Left FootEl blog de Oscar Arrivi que habla de informatica, programación en .NET y otras cosas que se me ocurran.Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.comBlogger16125tag:blogger.com,1999:blog-1553700282934663138.post-28143863647305933192013-09-13T12:11:00.001+02:002013-09-13T12:12:57.653+02:00Gestión de versiones en el GAC<p>Cuando se desarrolla una librería para ser distribuida públicamente es normal que esta pueda tener actualizaciones de versiones. Bien porque corrige algunos fallos o porque introduce nueva funcionalidad. Sobre el sistema de versionados ya hablé en un <a href="http://theartoftheleftfoot.blogspot.com/2011/02/versionado-de-ensamblados-de-net.html" target="_blank">post</a> anterior. </p> <p>En esta ocasión me quiero centrar en las actualizaciones automáticas de estos ensamblados de las librerías en el GAC. Me explico con un ejemplo. Supongamos que hemos publicado una librería (v1.0.0.0) para que pueda ser usada por múltiples aplicaciones y componentes. Sacamos una nueva versión que corrige algunos errores (v1.0.0.1) y que mantiene la compatibilidad con la versión anterior.</p> <p>Al ir firmadas con un <em>Strong Name</em>, las aplicaciones ignoran la nueva versión por completo, obligando a que estas deban ser recompiladas haciendo referencia a la nueva versión. Estamos dejando en manos de los desarrolladores que usan nuestras librerías la tarea de actualizar sus aplicaciones a la nueva versión. Si el número de aplicaciones es grande o el problema solucionado es grave (supongamos un agujero de seguridad), el escenario no es muy halagüeño.</p> <p>Lo ideal es que las aplicaciones se adapten automáticamente a las nuevas versiones de ensamblados sin que a priori se necesite la re-compilación de las aplicaciones. Y digo a priori porque deben ser los desarrolladores de las aplicaciones los que establezcan si realmente quieren este comportamiento o no.</p> <h3>Redirección de versiones de ensamblado</h3> <p>.Net nos permite este tipo de políticas a través de su redirección de ensamblados. Esto es, podemos indicar a una aplicación que use versiones más modernas de un ensamblado.</p> <p>Este tipo de políticas se pueden realizar en 3 niveles:</p> <ul> <li>A nivel de aplicación. Cada aplicación decide su política de actualizaciones. Es decir, que versión van a usar en caso de que haya varias versiones del mismo ensamblado, o si realmente no quieren que esta política se lleve a cabo. <li>A nivel de máquina. Este es poco frecuente de usar. Normalmente el administrador de la máquina decide si se debe usar una versión concreta o no de un ensamblado en las aplicaciones instaladas que la usan. <li>A nivel de proveedor. Los proveedores de las librerías definen en el GAC la política de versiones a usar. De manera que cuando deciden que dos versiones son compatibles, las aplicaciones usarán automáticamente la versión más moderna. Aun así, se puede indicar que una aplicación no siga esta política y que continúe usando la versión de ensamblado con la que fue creado.</li></ul> <p>En los tres niveles, el esquema para definir la política de redirección se realiza de la misma manera a través de XML:</p> <p><a href="http://lh3.ggpht.com/--rquhd9XspA/UjLkwN1IrmI/AAAAAAAAAkw/UcYUpItZizg/s1600-h/image%25255B10%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh3.ggpht.com/-L62XZDDqOHc/UjLkwpeaqLI/AAAAAAAAAk4/bNL0tbo3q7c/image_thumb%25255B5%25255D.png?imgmax=800" width="644" height="213"></a></p> <p align="center">PublicherPolicyFile.xml</p> <li><code><assemblyBinding></code> define el bloque de redirección y sólo puede haber uno por fichero de configuración. <li><code><dependantAssembly></code> puedes tener tantos bloques como necesites aunque cada uno sólo debe contener un elemento <code><assemblyIdentity>.</code> <li><code><assemblyIdentity></code> define el nombre del ensamblado a redireccionar y su publicKeyToken para poder ser identificado sin que haya ninguna ambigüedad. <li><code><bindingRedirect></code> define la redirección de la versión. en la parte de oldVersion soporta tanto una versión en concreto (como es el caso del ejemplo) como un rango de versiones. <p>No es necesario indicar de manera individual cada una de las versiones que queremos redirigir. También podemos indicar rangos de versiones y comodines:</p></li> <p><a href="http://lh6.ggpht.com/-J5U-dTSaXCw/UjLkxPcLtMI/AAAAAAAAAlA/Dul_q8lEPj0/s1600-h/image%25255B11%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="http://lh5.ggpht.com/-vZhlLjbtGWY/UjLkxrxUT1I/AAAAAAAAAlI/7_zZGdRbyak/image_thumb%25255B6%25255D.png?imgmax=800" width="644" height="202"></a></p> <p>Si queremos usar la política de actualizaciones a nivel de aplicación, basta con poner la configuración en el fichero de configuración de la aplicación tal y como se muestra en el código XML anterior.</p> <p>En caso de ser nosotros los que distribuyamos las librerías, necesitaremos crear un fichero de políticas de versionado a nivel de proveedor. A continuación se explica cómo hacerlo.</p> <h3></h3> <h3>Crear un fichero de políticas de proveedor</h3> <p>La creación de este fichero de políticas pasa por 3 sencillos pasos:</p> <ol> <li>Crear el fichero XML con la configuración de la política. Básicamente como se hizo en el ejemplo anterior. <li>Crear el ensamblado con la configuración de la política. <li>Añadir el ensamblado con la política de redirección al GAC.</li></ol> <p>El paso 1 es básicamente crear el fichero tal y como se define en el XML del ejemplo, donde para el ensamblado <em>ArriviSoft.RedirectingAssembllies.Library</em> se ha definido que todos aquellas aplicaciones que referencien a la versión 1.0.0.0, pueden utilizar la versión 1.0.0.1</p> <p>Para crear el ensamblado que contendrá la política de redirección debemos usar la herramienta <a href="http://msdn.microsoft.com/en-us/library/c405shex(v=vs.85).aspx" target="_blank">Assembly Linker (al.exe)</a> que viene con Visual Studio.</p> <p>Ejecutamos el siguiente comando:</p> <p><strong>al.exe /link:</strong>PublisherPolicyFile.xml <strong>/out:P</strong>olicy.1.0.ArriviSoft.RedirectingAssemblies.MyLibrary.dll <strong>/keyfile:</strong>ArriviSoft.snk <strong>/platform:</strong>x86</p> <p>donde:</p> <ul> <li><strong>/link:</strong> Indica el fichero xml con la política de redirección de ensamblados. <li><strong>/out:</strong> Indica el ensamblado que se va a generar y que contendrá la política de redirección. Atención con el formato de nombre de fichero pues debe ser:<br><strong>"Policy.</strong><em>majorVersion<strong>.</strong>minorVersion</em><strong>.</strong><em>NombreEnsamblado</em><strong>.dll"</strong> <li><strong>/keyfile:</strong> Fichero con el par de claves que además debe ser el mismo con el que se firme el ensamblado. <li><strong>/platform:</strong> Permite definir la plataforma de compilación (x86, amd64, ia64, o MSIL) para afinar mejor.<br>Desgraciadamente no podremos generar el mismo ensamblado de políticas si definimos distintas plataformas dado que el nombre del ensamblado resultante sería el mismo. Para solucionarlo bastaría con indicar la plataforma dentro del propio nombre de ensamblado.</li></ul> <p>Finalmente, una vez tengamos el ensamblado con la política de redirección, este ya puede ser usado en los equipos clientes. El instalador de nuestra librerías debe registrar en el GAC la nueva versión de ensamblados junto con el ensamblado de políticas usando el comando <a href="http://msdn.microsoft.com/en-us/library/ex0ss12c(v=vs.85).aspx" target="_blank">Global Assembly Cache tool (Gacutil.exe)</a></p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com0tag:blogger.com,1999:blog-1553700282934663138.post-36460129059124701022013-02-06T22:09:00.001+01:002013-02-06T22:13:49.900+01:00Enseñando licenciamiento de software con .NET a mi abuela<p><font size="2">Mi abuela es una señora muy adelantada a su tiempo y con muchas inquietudes sobre las nuevas tecnologías. No hace mucho le dio por empezar a programar en C# y a veces las dudillas que le surgen me las pregunta.</font></p> <p><font size="2">El otro día tuvimos una interesante conversación sobre el licenciamiento y me he decidido a intentar transcribir nuestra conversación en una entrada. Puede ser un poco larga pero de seguro que merece la pena.</font></p> <p><font color="#9b00d3" size="2"><em>- Pues mira nieto, el otro día hice una aplicación para gestionar residencias de ancianos. Parece que tiene buena acogida y que gusta, pero con el esfuerzo que me ha supuesto, no quisiera que se pusieran a copiársela entre las residencias. Que esos son muy listos para aprovecharse de los pobres ancianos. ¿Cómo puedo hacer para evitar que se lo copien mi aplicacioncita de unos a otros?</em></font></p> <p><font size="2">- Claro que si abuela. Me imagino lo mal que se tiene que sentir uno al encontrarse sus cosas en el emule, torrents o RapidShares de turno…</font></p> <p><em><font color="#9b00d3" size="2">- Niño, ¿de que me hablas? Yo me refería que se lo mandan por mail o los copian en CDs…</font></em></p> <p><font size="2">- Claro que si abuela. De hecho .NET tiene modelos que te permiten proteger, o mejor dicho, licenciar tus aplicaciones de manera muy sencilla y flexible. Si quieres te explico cómo se hace.</font></p> <p><font color="#9b00d3" size="2"><em>- Claro que si nieto. Pero explícamelo sencillito que tu ya sabes que a mi me cuesta un poco con estas edades…</em></font></p> <p><font size="2">- Bueno, bueno. A ver abuela. ¿Sabes de lo que hablo si digo atributos de clase?</font></p> <p><em><font color="#9b00d3" size="2">- Pues no estoy del todo segura. Anda, recuérdamelo. Y mejor si es con un ejemplillo.</font></em></p> <p><font size="2">- Bueno abuela. Los atributos de clase son unos modificadores que se colocan justo antes de definir las clases. Y se colocan entre corchetes. Hay muchos atributos de clase, de hecho, puedes usar varios para una misma clase. Por ejemplo, este atributo sirve para indicar que una clase es serializable.</font></p> <div class="csharpcode"><pre><span class="lnum"> 1: </span>[Serializable]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">class</span> Foo</pre><pre><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> <span class="rem">//...</span></pre><pre><span class="lnum"> 5: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">Como ya te he comentado, abuela, hay muchos más. Pero el que a nosotros nos interesa para el licenciamiento se llama <font face="Consolas">System.ComponentModel.LicenseProvider</font>.</font></p><br /><p><em><font color="#9b00d3" size="2">- O sea, que las protecciones se hacen usando ese atributo de clase, ¿no?</font></em></p><br /><p><font size="2">- Exactamente abuela. Cada vez que usemos este atributo en una clase, significará que esta estará protegida por una licencia.</font></p><br /><p><em><font color="#9b00d3" size="2">- Vale. Entonces la clase que yo quiera proteger debe quedar más o menos así, ¿no?</font></em></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span>[System.ComponentModel.LicenseProvider]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> Foo</pre><pre><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> <span class="rem">// ...</span></pre><pre><span class="lnum"> 5: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font color="#9b00d3" size="2"><em>Bien, nietecito. Hasta aquí lo entiendo. Aunque me temo que lo que sigue a partir de ahora será más complicado, ¿verdad?</em></font></p><br /><p><font size="2">- Que va abuela. Mira. Verás que sencillo. Cuando una clase viene con ese atributo, significa que va a recibir un objeto licencia. Esta licencia es una instancia de la clase <font face="Consolas">System.ComponentModel.License</font> y es muy sencillita:</font></p><br /><div class="csharpcode"><pre><span class="lnum"><span class="lnum"> 1: </span><span class="kwrd">namespace</span> System.ComponentModel</pre><pre><div class="csharpcode"><pre><span class="lnum"> 2: </span>{</pre><pre><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> License : IDisposable</pre><pre><span class="lnum"> 4: </span> {</pre><pre><span class="lnum"> 5: </span> <span class="kwrd">protected</span> License();</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">string</span> LicenseKey { get; }</pre><pre><span class="lnum"> 7: </span> <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">void</span> Dispose();</pre><pre><span class="lnum"> 8: </span> }</pre><pre><span class="lnum"> 9: </span>}</pre></div><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style></span></pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">Lo interesante de esta clase es que es abstracta, que implementa la interfaz <font face="Consolas">IDisposable</font> y su propiedad <font face="Consolas">LicenseKey</font> es de sólo lectura.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Ya veo. Entonces voy a recibir una instancia de "algo" que hereda de <font face="Consolas">System.ComponentModel.License</font>. Porque como dices, es una clase abstracta. Lo que no me queda claro es quién crea esa instancia ni cómo lo hace.</em></font></p><br /><p><font size="2">- Ahora vamos a eso, abuela. El sistema de licenciamiento que tiene .NET funciona de la siguiente manera: La clase que quieres proteger va a recibir un objeto que hereda de <font face="Consolas">License</font>. Este objeto <font face="Consolas">License</font> es generado por una entidad proveedora de licencias a través del gestor de licencias que tiene el framework de .NET.</font></p><br /><p><font size="2">Explicado de otra manera. Cuando la clase que quieres licenciar se esta creando, le pide al gestor de licencias (<font face="Consolas">System.ComponentModel.LicenseManager</font>) una licencia. Este entregará dicha licencia haciendo uso del generador o proveedor de licencias (<font face="Consolas">System.ComponentModel.LicenseProvider</font>)</font></p><br /><p><font color="#9b00d3" size="2"><em>- O sea,que <font face="Consolas">LicenseManager</font> va a retornar la licencia que a su vez retorne <font face="Consolas">LicenseProvider</font> a la clase a proteger. Aquí hay gato encerrado o hay algo que todavía no me has contado. La clase License es abstracta, asi que me falta una clase License que en cualquier caso, parece que siempre la devuelve LicenseProvider. Con lo cual, no veo que hay de bueno en todo esto.</em></font></p><br /><p><font size="2">- Claro abuela, te falta añadirle toda la lógica del sistema de licenciamiento. El framework de .NET te da la base y tu la completas.</font></p><br /><p><em><font color="#9b00d3" size="2">- Ay madre, que ya me asustas. ¿Cómo es eso de que yo tengo que poner la lógica a todo esto?</font></em></p><br /><p><font size="2">- Que no es para tanto, abuela. Lo que hay que hacer es crearse una clase que permita devolver las licencias que queramos usando la lógica que necesitemos. Y por supuesto, la licencia que esta devuelva podrá ser como queramos siempre y cuando herede de <font face="Consolas">System.ComponentModel.License</font>.</font></p><br /><p><font size="2">La clase generadora de licencias se le pasará como argumento al atributo de clase <font face="Consolas">System.ComponentModel.LicenseProvider</font>. De manera que el proveedor pueda invocar a nuestra clase generadora para obtener la licencia correspondiente.</font></p><br /><p><em><font color="#9b00d3" size="2">- Oye. ¿Y es muy difícil construir una clase generadora de licencias de estas? Me vas a tener que enseñar un ejemplo de esto que me estás contando, ¿eh?</font></em></p><br /><p><font size="2">- Bueno, tan difícil como queramos hacerlo nosotros. Microsoft de hecho nos dejó una creada muy sencillita. De hecho es tan sencillita que se aconseja no usarla para nuestros desarrollos. Esta clase es <font face="Consolas">System.LicenseProvider.LicFileLicenseProvider</font>.</font></p><br /><p><font size="2">Te enseño un ejemplo de cómo se usaría este generador:</font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span>[System.ComponentModel.LicenseProvider(<span class="kwrd">typeof</span>(System.ComponentModel.LicFileLicenseProvider))]</pre><pre><span class="lnum"> 2: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> Foo</pre><pre><span class="lnum"> 3: </span> {</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> System.ComponentModel.License _license = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> Foo()</pre><pre><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _license = System.ComponentModel.LicenseManager.Validate(<span class="kwrd">this</span>.GetType(), <span class="kwrd">this</span>);</pre><pre><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> }</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">Concretamente <font face="Consolas">LicFileLicenseProvider</font> funciona de la siguiente manera:</font></p><br /><p><font size="2">1. En la carpeta donde se encuentra el ensamblado con la clase a proteger, busca un fichero con extensión .lic y nombre igual que el nombre del namespace con la clase a proteger, un punto y el nombre de la clase a proteger.</font></p><br /><p><font size="2">2. El fichero .lic debe tener el siguiente contenido de texto: "Namespace+clase is a licensed component.", donde Namespace + clase es el nombre del namespace de la clase que queremos proteger.</font></p><br /><p><font size="2"><em><font color="#9b00d3">- Entiendo, entiendo. Pero no veo por qué no quieres que se use ese tipo de proveedor de licencias.</font></em> </font></p><br /><p><font size="2">- ¡Abuela! Cualquiera que supiese que estas usando ese licenciamiento puede crearse manualmente el fichero .lic y… adiós protección.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Bueno, bueno. Por cierto, ¿qué pasa si el torpe de turno va y borra o modifica el contenido de ese fichero?</em></font></p><br /><p><font size="2">- En ese caso, la llamada en el constructor de tu clase a Validate() fallará y lanzará una excepción de tipo <font face="Consolas">System.ComponentModel.LicenseException</font>. Si no la capturas, el licenciamiento habrá funcionado, puesto que no se ha creado ninguna instancia de la clase protegida, pero posiblemente el mensaje de error que lance la aplicación no será muy descriptivo para el usuario.</font></p><br /><p><font size="2">Las excepciones, cuando se producen, suelen ser bastante lentas, así que si únicamente quieres saber si existe licencia, puedes usar el método IsValid, en lugar de Validate. Este te retornará un booleano indicando si la licencia es valida o no. Pero no te devolverá ninguna licencia.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Entonces, realmente no necesito ninguna clase License. Me podría bastar con llamar a IsVaild y así saber si se puede ejecutar o no la aplicación, ¿no?</em></font></p><br /><p><font size="2">- Bueno, abuela, de eso se trata. El modelo es así de flexible. Si no lo necesitas, no lo uses. Pero fíjate lo que se puede llegar a montar. Imagínate que a pesar de no tener una licencia buena, quisieras permitir que la aplicación se ejecutara. Por ejemplo, como una versión de prueba. O que dependiendo de la licencia, se pudieran ejecutar unas u otras opciones de la aplicación.</font></p><br /><p><font color="#9b00d3" size="2"><em>- ¡Pues claro! Así si que sería fácil, por ejemplo, vender por separado el modulo de gestión de enfermeros y por otro lado la gestión de las medicinas… ¡Realmente interesante!</em></font></p><br /><p><font size="2">- Exacto abuela. Y en la propiedad <font face="Consolas">License.LicenseKey</font> podrías poner el identificador del cliente.</font></p><br /><p><font size="2">Resumiendo. Tenemos 4 actores en esto de la gestión de licencias: La clase que queremos licenciar, el tipo de licencia, el proveedor de licencias y el gestor de licencias.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Si, lo voy entendiendo. Pero, ¿cómo podría crearme mi propio proveedor de licencias?</em></font></p><br /><p><font size="2">- No es difícil, abuela. Basta con crearse una clase que herede de LicenseProvider y otra que herede de License:</font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">using</span> System.ComponentModel;</pre><pre><span class="lnum"> 2: </span> </pre><pre><span class="lnum"> 3: </span><span class="rem">/// My License Provider class</span></pre><pre><span class="lnum"> 4: </span><span class="kwrd">class</span> MyLicenseProvider : LicenseProvider</pre><pre><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> License GetLicense(LicenseContext context, Type type, <span class="kwrd">object</span> instance, <span class="kwrd">bool</span> allowExceptions)</pre><pre><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MyLicense();</pre><pre><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span>}</pre><pre><span class="lnum"> 11: </span> </pre><pre><span class="lnum"> 12: </span><span class="rem">// My License class</span></pre><pre><span class="lnum"> 13: </span><span class="kwrd">class</span> MyLicense : License</pre><pre><span class="lnum"> 14: </span>{</pre><pre><span class="lnum"> 15: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">string</span> LicenseKey</pre><pre><span class="lnum"> 16: </span> {</pre><pre><span class="lnum"> 17: </span> get { <span class="kwrd">return</span> <span class="str">"Some license key"</span>; }</pre><pre><span class="lnum"> 18: </span> }</pre><pre><span class="lnum"> 19: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">Este es un ejemplo muy sencillo. Realmente no estamos haciendo ninguna comprobación de nada. Siempre devuelve una licencia buena.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Ya creo que me voy enterando. De todas maneras no estoy muy segura de la propiedad LicenseKey de la licencia. Si es de sólo lectura y toda la lógica para la generación de la licencia reside en el proveedor. Entonces le tendré que pasar el valor del LicenseKey a través del constructor, ¿no? Sino, no lo veo yo. Luego dependiendo de ese LicenseKey la aplicación permitiría o no hacer determinadas acciones. ¿Cierto?</em></font></p><br /><p><font color="#9b00d3" size="2"><em>Más o menos esta sería mi clase Licencia, que recibiría el License Key a través del constructor:</em></font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">class</span> MyLicense : License</pre><pre><span class="lnum"> 2: </span>{</pre><pre><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">string</span> _licenseKey = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 4: </span> </pre><pre><span class="lnum"> 5: </span> <span class="kwrd">public</span> MyLicense(<span class="kwrd">string</span> licenseKey)</pre><pre><span class="lnum"> 6: </span> {</pre><pre><span class="lnum"> 7: </span> _licenseKey = licenseKey;</pre><pre><span class="lnum"> 8: </span> }</pre><pre><span class="lnum"> 9: </span> </pre><pre><span class="lnum"> 10: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">string</span> LicenseKey</pre><pre><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> get { <span class="kwrd">return</span> _licenseKey; }</pre><pre><span class="lnum"> 13: </span> }</pre><pre><span class="lnum"> 14: </span> </pre><pre><span class="lnum"> 15: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Dispose()</pre><pre><span class="lnum"> 16: </span> {</pre><pre><span class="lnum"> 17: </span> }</pre><pre><span class="lnum"> 18: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font color="#9b00d3" size="2"><em>Mi clase Proveedora de licencias que, a modo de ejemplo, devuelve una licencia siempre con la misma clave:</em></font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">using</span> System.ComponentModel;</pre><pre><span class="lnum"> 2: </span> </pre><pre><span class="lnum"> 3: </span><span class="kwrd">class</span> MyLicenseProvider : LicenseProvider</pre><pre><span class="lnum"> 4: </span>{</pre><pre><span class="lnum"> 5: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> License GetLicense(LicenseContext context, Type type, <span class="kwrd">object</span> instance, <span class="kwrd">bool</span> allowExceptions)</pre><pre><span class="lnum"> 6: </span> {</pre><pre><span class="lnum"> 7: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MyLicense(<span class="str">"Full license"</span>);</pre><pre><span class="lnum"> 8: </span> }</pre><pre><span class="lnum"> 9: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2"></font></p><br /><p><em><font color="#9b00d3" size="2">Y por último mi clase protegida. En este caso quiero proteger la llamada a DoSomething chequeando el valor de la clave de licencia:</font></em></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">using</span> System.ComponentModel;</pre><pre><span class="lnum"> 2: </span> </pre><pre><span class="lnum"> 3: </span>[LicenseProvider(<span class="kwrd">typeof</span>(MyLicenseProvider))]</pre><pre><span class="lnum"> 4: </span><span class="kwrd">public</span> <span class="kwrd">class</span> Foo</pre><pre><span class="lnum"> 5: </span>{</pre><pre><span class="lnum"> 6: </span> <span class="kwrd">private</span> License _license = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 7: </span> </pre><pre><span class="lnum"> 8: </span> <span class="kwrd">public</span> Foo()</pre><pre><span class="lnum"> 9: </span> {</pre><pre><span class="lnum"> 10: </span> _license = LicenseManager.Validate(<span class="kwrd">this</span>.GetType(), <span class="kwrd">this</span>);</pre><pre><span class="lnum"> 11: </span> }</pre><pre><span class="lnum"> 12: </span> </pre><pre><span class="lnum"> 13: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> DoSomething()</pre><pre><span class="lnum"> 14: </span> {</pre><pre><span class="lnum"> 15: </span> <span class="kwrd">if</span> (_license.LicenseKey != <span class="str">"Full license"</span>)</pre><pre><span class="lnum"> 16: </span> {</pre><pre><span class="lnum"> 17: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> LicenseException(<span class="kwrd">this</span>.GetType(), <span class="kwrd">this</span>, </pre><pre><span class="lnum"> 18: </span> <span class="str">"You don´t have a valid license to invoke this method."</span>);</pre><pre><span class="lnum"> 19: </span> }</pre><pre><span class="lnum"> 20: </span> </pre><pre><span class="lnum"> 21: </span> <span class="rem">// Do something here ...</span></pre><pre><span class="lnum"> 22: </span> }</pre><pre><span class="lnum"> 23: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">- Bueno abuela, claro que lo podrías hacer así. Pero te estarías cargando toda la filosofía del modelo de licencias. Si en cualquier momento quisieras cambiar la forma de proteger tu aplicación, tendrías que modificar en todos los sitios de la clase protegida donde se comprueba el valor de la clave de licencia.</font></p><br /><p><font size="2">Realmente a la aplicación, o en tu ejemplo, a la clase protegida, le da absolutamente igual el valor de LicenseKey. El hecho de que se haya podido llamar a la función ya significa que hay una licencia valida pues el constructor ya la ha validado.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Ah! Entonces también la lógica de saber si las claves de licencia son buenas o no, de si se puede ejecutar cierta función y demás, se hace también en el proveedor. ¿Cierto?</em></font></p><br /><p><font size="2">- Exactamente. Piensa en tu licencia como un contrato o un permiso de utilización de la clase protegida. Si tiene licencia, funciona, sino, ni siquiera puede existir. Además, si fuera necesario, podría indicar determinadas limitaciones.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Comprendo, comprendo. Es como tu carnet de conducir, ¿no? Si tienes licencia, puedes llevar el coche, y si no, no. Pero además, tu carnet también te limita los vehículos que puedes conducir, ¿verdad?</em></font></p><br /><p><font size="2">- Eso es abuela, muy buen ejemplo. Me lo tengo que apuntar para cuando se lo tenga que explicar a otras personas. Jejeje.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Entonces el ejemplo que he dicho antes no sirve. Mejor sería poner algo así:</em></font></p><br /><p><em><font color="#9b00d3" size="2">Mi licencia es capaz de indicar si se está en modo demostración. En este modo habrá operaciones que no se pueden ejecutar:</font></em></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">class</span> MyLicense : License</pre><pre><span class="lnum"> 2: </span>{</pre><pre><span class="lnum"> 3: </span> <span class="kwrd">private</span> <span class="kwrd">string</span> _licenseKey = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 4: </span> </pre><pre><span class="lnum"> 5: </span> <span class="kwrd">public</span> MyLicense(<span class="kwrd">string</span> licenseKey, <span class="kwrd">bool</span> isDemoVersion)</pre><pre><span class="lnum"> 6: </span> {</pre><pre><span class="lnum"> 7: </span> _licenseKey = licenseKey;</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">this</span>.IsDemoVersion = isDemoVersion;</pre><pre><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> </pre><pre><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">string</span> LicenseKey</pre><pre><span class="lnum"> 12: </span> {</pre><pre><span class="lnum"> 13: </span> get { <span class="kwrd">return</span> _licenseKey; }</pre><pre><span class="lnum"> 14: </span> }</pre><pre><span class="lnum"> 15: </span> </pre><pre><span class="lnum"> 16: </span> <span class="kwrd">public</span> <span class="kwrd">bool</span> IsDemoVersion { get; <span class="kwrd">private</span> set; }</pre><pre><span class="lnum"> 17: </span> </pre><pre><span class="lnum"> 18: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Dispose()</pre><pre><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> }</pre><pre><span class="lnum"> 21: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font color="#9b00d3" size="2"><em>Mi clase proveedora de licencias determina si la licencia a generar es de tipo demostración o no. En mi ejemplo, simplemente es de demostración a partir de 2013:</em></font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">class</span> MyLicenseProvider : LicenseProvider</pre><pre><span class="lnum"> 2: </span>{</pre><pre><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> MyLicense GetLicense(LicenseContext context, Type type, <span class="kwrd">object</span> instance, <span class="kwrd">bool</span> allowExceptions)</pre><pre><span class="lnum"> 4: </span> {</pre><pre><span class="lnum"> 5: </span> <span class="rem">// Demo version since 2013</span></pre><pre><span class="lnum"> 6: </span> <span class="kwrd">if</span> (DateTime.Now.CompareTo(<span class="kwrd">new</span> DateTime(2013, 1, 1)) > 0)</pre><pre><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MyLicense(<span class="str">"Full license"</span>, <span class="kwrd">true</span>);</pre><pre><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> <span class="kwrd">else</span></pre><pre><span class="lnum"> 11: </span> {</pre><pre><span class="lnum"> 12: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MyLicense(<span class="str">"Full license"</span>, <span class="kwrd">false</span>);</pre><pre><span class="lnum"> 13: </span> }</pre><pre><span class="lnum"> 14: </span> }</pre><pre><span class="lnum"> 15: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font color="#9b00d3" size="2"><em>Por último, la clase que estoy protegiendo ya no tiene que tener ninguna lógica respecto a la licencia. Sólo chequear que no está en modo demostración:</em></font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span>[LicenseProvider(<span class="kwrd">typeof</span>(MyLicenseProvider))]</pre><pre><span class="lnum"> 2: </span><span class="kwrd">public</span> <span class="kwrd">class</span> Foo</pre><pre><span class="lnum"> 3: </span>{</pre><pre><span class="lnum"> 4: </span> <span class="kwrd">private</span> MyLicense _license = <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 5: </span> </pre><pre><span class="lnum"> 6: </span> <span class="kwrd">public</span> Foo()</pre><pre><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> _license = LicenseManager.Validate(<span class="kwrd">this</span>.GetType(), <span class="kwrd">this</span>) <span class="kwrd">as</span> MyLicense;</pre><pre><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> </pre><pre><span class="lnum"> 11: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> DoSomething()</pre><pre><span class="lnum"> 12: </span> {</pre><pre><span class="lnum"> 13: </span> <span class="rem">// Demo version cannot execute this code!!!</span></pre><pre><span class="lnum"> 14: </span> <span class="kwrd">if</span> (_license.IsDemoVersion)</pre><pre><span class="lnum"> 15: </span> {</pre><pre><span class="lnum"> 16: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> LicenseException(<span class="kwrd">this</span>.GetType(), <span class="kwrd">this</span>, </pre><pre><span class="lnum"> 17: </span> <span class="str">"You don´t have a valid license to invoke this method."</span>);</pre><pre><span class="lnum"> 18: </span> }</pre><pre><span class="lnum"> 19: </span> </pre><pre><span class="lnum"> 20: </span> <span class="rem">// Do something here ...</span></pre><pre><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span>}</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">- Mucha mejor pinta, abuela. Tampoco podemos olvidar que la llamada a Validate nos ofrece otros argumentos la mar de interesantes. Vamos a verlos.</font></p><br /><ul><br /><li><font size="2"><strong>LicenseContext context</strong>. Este objeto nos da el contexto en el cual se va a usar la clase a proteger. Quizás su propiedad más importante sea UsageMode, el cual nos va a permitir saber si estamos ejecutando una aplicación normal de escritorio o bien si estamos usando la clase en modo diseño dentro de Visual Studio. Por ejemplo, para licenciar componentes o controles gráficos para otros desarrolladores.</font> <br /><li><font size="2"><strong>Type type.</strong> Nos da el tipo de la clase a licenciar. Así podemos usar el mismo proveedor de licencias para proteger varias clases.</font> <br /><li><font size="2"><strong>object instance.</strong> Si no fuese sufieciente saber el tipo, tambien tenemos a nuestra disposición la instancia de la clase a proteger.</font> <br /><li><font size="2"><strong>bool allowExceptions. </strong>Nos indica si debemos lanzar una excepción LicenseException en caso de no cumplir con la licencia. Y esto tiene sentido pues acuerdate de la llamada IsValid que no lanza excepciones. Básicamente Validate e IsValid usan la llamada a esta función. Una permite excepciones y la otra no.</font></li></ul><br /><p><font size="2">Sin meternos en historias de dar soporte a distintos tipos de clases a proteger ni nada de eso, tu clase proveedora de licencias quedaría mejor así:</font></p><br /><div class="csharpcode"><pre><span class="lnum"> 1: </span><span class="kwrd">class</span> MyLicenseProvider : LicenseProvider</pre><pre><span class="lnum"> 2: </span>{</pre><pre><span class="lnum"> 3: </span> <span class="kwrd">public</span> <span class="kwrd">override</span> MyLicense GetLicense(LicenseContext context, Type type, <span class="kwrd">object</span> instance, <span class="kwrd">bool</span> allowExceptions)</pre><pre><span class="lnum"> 4: </span> {</pre><pre><span class="lnum"> 5: </span> <span class="rem">// Full license until 2013</span></pre><pre><span class="lnum"> 6: </span> <span class="kwrd">if</span> (DateTime.Now.CompareTo(<span class="kwrd">new</span> DateTime(2013, 1, 1)) < 0)</pre><pre><span class="lnum"> 7: </span> {</pre><pre><span class="lnum"> 8: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MyLicense(<span class="str">"Full license"</span>, <span class="kwrd">false</span>);</pre><pre><span class="lnum"> 9: </span> }</pre><pre><span class="lnum"> 10: </span> <span class="rem">// Demo version between 2013 - 2014</span></pre><pre><span class="lnum"> 11: </span> <span class="kwrd">else</span> <span class="kwrd">if</span> (DateTime.Now.CompareTo(<span class="kwrd">new</span> DateTime(2014, 1, 1)) < 0)</pre><pre><span class="lnum"> 12: </span> {</pre><pre><span class="lnum"> 13: </span> <span class="kwrd">return</span> <span class="kwrd">new</span> MyLicense(<span class="str">"Demo version"</span>, <span class="kwrd">true</span>);</pre><pre><span class="lnum"> 14: </span> }</pre><pre><span class="lnum"> 15: </span> <span class="rem">// No license from 2014</span></pre><pre><span class="lnum"> 16: </span> <span class="kwrd">else</span></pre><pre><span class="lnum"> 17: </span> {</pre><pre><span class="lnum"> 18: </span> <span class="kwrd">if</span> (allowExceptions)</pre><pre><span class="lnum"> 19: </span> {</pre><pre><span class="lnum"> 20: </span> <span class="kwrd">throw</span> <span class="kwrd">new</span> LicenseException(type, instance, <span class="str">"Invalid license."</span>);</pre><pre><span class="lnum"> 21: </span> }</pre><pre><span class="lnum"> 22: </span> <span class="kwrd">else</span></pre><pre><span class="lnum"> 23: </span> {</pre><pre><span class="lnum"> 24: </span> <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre><pre><span class="lnum"> 25: </span> }</pre><pre><span class="lnum"> 26: </span> }</pre><pre><span class="lnum"> 27: </span> }</pre></div><br /><style type="text/css">.csharpcode, .csharpcode pre<br />{<br /> font-size: small;<br /> color: black;<br /> font-family: consolas, "Courier New", courier, monospace;<br /> background-color: #ffffff;<br /> /*white-space: pre;*/<br />}<br />.csharpcode pre { margin: 0em; }<br />.csharpcode .rem { color: #008000; }<br />.csharpcode .kwrd { color: #0000ff; }<br />.csharpcode .str { color: #006080; }<br />.csharpcode .op { color: #0000c0; }<br />.csharpcode .preproc { color: #cc6633; }<br />.csharpcode .asp { background-color: #ffff00; }<br />.csharpcode .html { color: #800000; }<br />.csharpcode .attr { color: #ff0000; }<br />.csharpcode .alt <br />{<br /> background-color: #f4f4f4;<br /> width: 100%;<br /> margin: 0em;<br />}<br />.csharpcode .lnum { color: #606060; }<br /></style><br /><br /><p><font size="2">Y así nuestra clase protegida puede usar IsValid en lugar de Validate sin preocuparse de si se van a producir excepciones.</font></p><br /><p><font size="2">Es muy importante que te quede claro que toda la lógica de la licencia debe quedar dentro de la clase de generadora de licencias. Esta puede hacer todo lo que se te ocurra. Desde leer un fichero de texto como hace LicFileLicenseProvider, hasta conectarse a un servidor remoto en otro continente para obtener la licencia.</font></p><br /><p><font color="#9b00d3" size="2"><em>- Pues si nietecito, ya tengo más o menos claro todo esto de las licencias. Ya puedo estar segura de que nadie se va a copiar mi programa de residencias de ancianos.</em></font></p><br /><p><font size="2">- Me alegro de haberte ayudado abuela. De todas maneras, siempre habrá alguien que encuentre la manera de saltarse la licencia. Pero todo dependerá de lo importante que sea lo que tengamos que proteger y el tiempo que le queramos dedicar poniendo trampas y demás dificultades a estos individuos. Recuerda, no hace falta crear la super licencia que protege los sistemas del Pentágono. La mayoría de las personas lo dará por imposible sólo al ver que ya tiene licencia. Y eso es lo importante.</font></p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com11tag:blogger.com,1999:blog-1553700282934663138.post-65760413529098422312012-02-18T11:25:00.001+01:002012-02-18T11:25:14.532+01:00El nuevo logotipo de Windows 8<p>Microsoft ha hecho público en su página de <a href="http://windowsteamblog.com/windows/b/bloggingwindows/archive/2012/02/17/redesigning-the-windows-logo.aspx" target="_blank">The Windows Blog</a> lo que parece que será el diseño del nuevo logotipo de Windows 8.</p> <p>Parece que el resultado no ha dejado indiferente al personal y corren ríos de <em>posts</em> comentando el nuevo diseño.</p> <p><img title="Logotip Windows 8" alt="Logotipo Windows 8" src="http://windowsteamblog.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-59-23-metablogapi/6201.Win8Logo_5F00_01_5F00_thumb_5F00_23669D8A.jpg"></p> <p>Han querido infundirle el espíritu de su nueva interfaz gráfica <a href="http://en.wikipedia.org/wiki/Metro_(design_language)" target="_blank">Metro</a>, dotándole de líneas simplistas, planas y monocromáticas.</p> <p>A mi desde luego no me gusta. Recuerda mucho al logotipo de <a href="http://es.wikipedia.org/wiki/Windows_1.0" target="_blank">Windows 1.0</a>, aunque desde mi punto de vista, no lo mejora.</p> <p><img title="Logotipo Windows 1.0" alt="Logotipo de Windows 1.0" src="http://windowsteamblog.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-59-23-metablogapi/6318.2_5F00_thumb_5F00_0B0559FD.png"></p> <p>Creo que lo han hecho demasiado simple. Han querido condensar demasiado significado en 4 cuadrados con algo de perspectiva. Lo cual me hace recordar una celebre frase:</p> <blockquote> <p>Everything should be made as simpler as possible, but not simpler.</p> <p><em>Albert Einstein</em></p></blockquote> <p>De hecho, una de las responsables de la empresa encargada de los diseños de logos para Windows hizo esta sencilla pregunta: “<em>your name is Windows. Why are you a flag?” </em></p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com0tag:blogger.com,1999:blog-1553700282934663138.post-25678523499614303142011-06-05T23:43:00.002+02:002011-06-05T23:49:54.779+02:00Primeras impresiones de Windows 8Microsoft ha presentado hace muy pocos días lo que va a ser su siguiente generación de sistemas operativos. Por ahora se llama Windows 8 y se estima que saldrá a lo largo de 2012.<br />
<div class="wlWriterEditableSmartContent" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:57297880-9892-4fc6-821d-6452813c1311" style="display: inline; float: none; margin: 0px; padding: 0px;"><div id="a3b63be1-75ad-4bf9-b0fb-12c3def87fa2" style="display: inline; margin: 0px; padding: 0px;"><div></div></div></div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/p92QfWOw88I?feature=player_embedded' frameborder='0'></iframe></div><div class="separator" style="clear: both; text-align: center;"><a href="http://www.youtube.com/watch?v=p92QfWOw88I">link del video</a></div>Está inspirado en la interfaz de usuario de su nuevo sistema operativo para moviles Windows Phone 7 (WP7). Lo que le permite tener más información de numerosas fuentes en una sola pantalla (HUBs).<br />
El nuevo Windows 8 está preparado tanto para funcionar sobre plataformas clásicas de escritorio Intel de 32 y 64 bits como sobre plataformas ARM. Mejor adaptadas a dispositivos pequeños como tabletas y micro-portátiles. Interesante ver que durante el video, no se nombra a los dispositivos <em>tablets </em>(tabletas), sino a los <em>slates</em>, que viene a significar pizarras. Lo malo es que parece que las aplicaciones clásicas de Windows no serán compatibles con la versión de la plataforma ARM. Lo cual significa que en muchos casos será necesario sacar dos versiones de las aplicaciones. Y esperemos que sea tan facil como cambiar en el IDE de desarrollo una opción de plataforma, porque sino…<br />
Hay que tener muy claro que el video es una demo. Los que hayan preparado alguna vez una demo sabrán a lo que me refiero. Normalmente lo que se muestra está preparado. Y muchos de los botones sean figurantes, que no tengan funcionalidad, o con una probabilidad alta de fallos. Y no creo que sea una casualidad que hayan sacado esto con tan pocos días de antelación a la presentación de Apple de su Sistema Operativo <em>Lion</em>.<br />
Tras ver el video, mi primera impresión al ver el video fue el de estar viendo una pantalla de movil con Windows Phone 7 de tamaño gigante. El movimiento por las pantallas es fluido y el contenido de cada cuadro preciso. De esta manera no es necesario abrir toda la aplciación para ver si hay algún cambio en su estado. Personalmente me parece una buena apuesta anti-Apple. Habrá que saber si el gran público la adopta de buena gana.<br />
En cualquier caso, Windows 8 puede funcionar tambien con el escritorio clásico de Windows. Por las pocas pantallas que aparecen en el video, se parece bastante a Windows 7 con algún retoquito estético.<br />
Si me ha defraudado un poco el que digan que las aplicaciones “táctiles” estén basadas en HTML5+JavaScript. No puedo entender que Microsoft, teniendo una plataforma de desarrollo como es .NET y Silverlight, no base las aplicaciones de su nueva interfaz en estos lengajes. Aunque es cierto que he leido por ahí (no recuerdo exactamente donde) que los harían compatible.<br />
En definitiva, sólo hemos visto un video de una demo. Veremos lo que nos deparan los días y otras demos que se hagan sobre el nuevo sistema operativo de Microsoft.Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com3tag:blogger.com,1999:blog-1553700282934663138.post-76221465843711321712011-02-15T00:22:00.001+01:002011-02-15T00:22:05.244+01:00YAML. Un lenguaje estructurado pero no de marcado<p>YAML, cuyas iniciales dicen YAML Ain't Markage Language, es un lenguaje que me ha sorprendido. Lo descubrí a raíz de un artículo en el que comentaban su uso en la introducción de estructuras de datos por personas que no estaban relacionadas con el desarrollo software.<br>Y es que es cierto que estamos muy acostumbrados a usar lenguaje XML o derivados, del que .NET tiene muchas librerias, o incluso JSON para otras plataformas. <br>Xml no está pensado realmente para ser leido por una persona. Aunque para ficheros pequeños y con no demasiadas estructuras anidadas, podamos apañarnos bien.</p> <p>YAML es un lenguaje estructurado, pero sin etiquetas. Lo cual lo hace extremadamente fácil de leer para una persona. Definido a partir de una serie de reglas muy símples que hace que escribir un documento con este formato sea realmente fácil.</p> <p>Las andaduras de este lenguaje comenzaron en Mayo de 2001 por Clark Evans. Y actualmente está en la versión de especificación 1.2. La página de referencia la podeis encontrar <a href="http://www.yaml.org/" target="_blank">aquí</a>. Y que precisamente tiene todo su contenido escrito con estructura YAML.</p> <p>Una pequeña lista de sus características son:</p> <ul> <li>YAML tiene un formato de lectura sencillo para las personas.</li> <li>YAML es conciso y compacto.</li> <li>YAML es expresivo y extensible.</li> <li>YAML no es un lenguaje de marcado!</li></ul> <p>Si esta lista la quisiera expresar con XML necesitaría algo así:</p> <p><font face="Courier New"><ul><br> <li>YAML tiene un formato de lectura sencillo para las personas.</li><br> <li>YAML es conciso y compacto.</li><br> <li>YAML es expresivo y extensible.</li><br> <li>YAML no es un lenguaje de marcado!</li><br><ul></font></p> <p>En YAML se definiría de la siguiente manera:</p> <p><font face="Courier New">- YAML tiene un formato de lectura sencillo para las personas.<br></font><font face="Courier New">- YAML es conciso y compacto.<br></font><font face="Courier New">- YAML es expresivo y extensible.<br></font><font face="Courier New">- YAML no es un lenguaje de marcado!</font></p> <p>Otros ejemplos de estructuras que pueden hacerse con YAML:</p> <p><strong>Diccionarios:</strong></p> <p>---<br>Clave 1: Valor de la clave 1.<br>Clave 2: Valor de la clave 2.<br>Clave 3: Valor de la clave 3.</p> <p><strong>Listas de diccionarios anidados:</strong></p> <p>---<br>- Clave 1: Valor de la clave 1.<br>- Clave 2:<br> Subclave 1: Valor de la Clave 2, Subclave 1.<br> Subclave 2: Valor de la Clave 2, Subclave 2.</p> <p><strong>Mapas:</strong></p> <p>---<br>Seat Leon: {Color: Rojo, Carburante: Diesel, Velocidad Máxima: 185 Km/h, Kilometros: 25000}<br>Nissan Micra: {Color: Blanco, Carburante: Hibrido, Velocidad Máxima: 172 Km/h, Kilometros: 12000}<br> </p> <p>Existen implementaciones de este formato en muchos lenguajes, incluyendo .NET, el cual dispone de una implementación en <a href="http://yaml.codeplex.com/" target="_blank">Codeplex</a> y que acompaña con un minitutorial de <a href="http://yaml.codeplex.com/wikipage?title=Yaml%20in%205%20minutes" target="_blank">Yaml en 5 minutos</a>.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com0tag:blogger.com,1999:blog-1553700282934663138.post-9682625902999271662011-02-09T23:52:00.001+01:002011-02-09T23:56:10.968+01:00Versionado de ensamblados de .NET<p>Uno de los temas que menos cuidado se le suele prestar en el desarrollo de software es el del versionado de componentes. Y es que gracias a las versiones, podemos saber exactamente cuando se compiló un ensamblado (e incluso saber la etiqueta de la versión de código fuente que la generó) y si hay otros ensamblados que son compatibles.</p> <p>En .NET, las versiones se establecen en el fichero AssemblyInfo.cs que se puede encontrar dentro de la carpeta de “Properties” del proyecto:</p> <div style="font-family: consolas; background: #dddddd; color: black; font-size: 11pt"> <p style="margin: 0px"><span style="color: #2b91af">    1</span> <span style="color: blue">using</span> System.Reflection;</p> <p style="margin: 0px"><span style="color: #2b91af">    2</span> <span style="color: blue">using</span> System.Runtime.InteropServices;</p> <p style="margin: 0px"><span style="color: #2b91af">    3</span> </p> <p style="margin: 0px"><span style="color: #2b91af">    4</span> <span style="color: green">// General Information about an assembly is controlled through the following </span></p> <p style="margin: 0px"><span style="color: #2b91af">    5</span> <span style="color: green">// set of attributes. Change these attribute values to modify the information</span></p> <p style="margin: 0px"><span style="color: #2b91af">    6</span> <span style="color: green">// associated with an assembly.</span></p> <p style="margin: 0px"><span style="color: #2b91af">    7</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyTitle</span>(<span style="color: #a31515">"Project Title"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">    8</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyDescription</span>(<span style="color: #a31515">"Some description"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">    9</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyConfiguration</span>(<span style="color: #a31515">"Debug"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   10</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyCompany</span>(<span style="color: #a31515">"My Company of Software"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   11</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyProduct</span>(<span style="color: #a31515">"Product Name"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   12</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyCopyright</span>(<span style="color: #a31515">"Copyright © My Company, 2011"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   13</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyTrademark</span>(<span style="color: #a31515">""</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   14</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyCulture</span>(<span style="color: #a31515">""</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   15</span> </p> <p style="margin: 0px"><span style="color: #2b91af">   16</span> <span style="color: green">// Setting ComVisible to false makes the types in this assembly not visible </span></p> <p style="margin: 0px"><span style="color: #2b91af">   17</span> <span style="color: green">// to COM components.  If you need to access a type in this assembly from </span></p> <p style="margin: 0px"><span style="color: #2b91af">   18</span> <span style="color: green">// COM, set the ComVisible attribute to true on that type.</span></p> <p style="margin: 0px"><span style="color: #2b91af">   19</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">ComVisible</span>(<span style="color: blue">false</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   20</span> </p> <p style="margin: 0px"><span style="color: #2b91af">   21</span> <span style="color: green">// The following GUID is for the ID of the typelib if this project is exposed to COM</span></p> <p style="margin: 0px"><span style="color: #2b91af">   22</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">Guid</span>(<span style="color: #a31515">"f446dcb3-68fb-49d9-a057-fe50383ce4c3"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   23</span> </p> <p style="margin: 0px"><span style="color: #2b91af">   24</span> <span style="color: green">// Version information for an assembly consists of the following four values:</span></p> <p style="margin: 0px"><span style="color: #2b91af">   25</span> <span style="color: green">//</span></p> <p style="margin: 0px"><span style="color: #2b91af">   26</span> <span style="color: green">//      Major Version</span></p> <p style="margin: 0px"><span style="color: #2b91af">   27</span> <span style="color: green">//      Minor Version </span></p> <p style="margin: 0px"><span style="color: #2b91af">   28</span> <span style="color: green">//      Build Number</span></p> <p style="margin: 0px"><span style="color: #2b91af">   29</span> <span style="color: green">//      Revision</span></p> <p style="margin: 0px"><span style="color: #2b91af">   30</span> <span style="color: green">//</span></p> <p style="margin: 0px"><span style="color: #2b91af">   31</span> <span style="color: green">// You can specify all the values or you can default the Build and Revision Numbers </span></p> <p style="margin: 0px"><span style="color: #2b91af">   32</span> <span style="color: green">// by using the '*' as shown below:</span></p> <p style="margin: 0px"><span style="color: #2b91af">   33</span> <span style="color: green">// [assembly: AssemblyVersion("1.0.*")]</span></p> <p style="margin: 0px"><span style="color: #2b91af">   34</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyVersion</span>(<span style="color: #a31515">"1.0.0.0"</span>)]</p> <p style="margin: 0px"><span style="color: #2b91af">   35</span> [<span style="color: blue">assembly</span>: <span style="color: #2b91af">AssemblyFileVersion</span>(<span style="color: #a31515">"1.0.0.0"</span>)]</p> </div> <p>Las últimas líneas del fichero están relacionadas con la versión del ensamblado y con la versión del fichero.</p> <h3>AssemblyFileVersion</h3> <p>Su principal objetivo es el de identificar de manera uniequivoca a un ensamblado. A partir de los valores de la versión podemos saber la versión de código fuente que compiló el ensamblado. Tambien nos permite reconocer si se trata de una versión de “Service Pack” o de “Hotfix”.</p> <p>Las versiones de un fichero están basadas en un código de 4 números. El uso de cada uno de los valores depende de cada equipo de desarrollo. Personalmente, les doy el siguiente significado a los valores de versión:</p> <ul> <li><strong>Major Version: </strong>Versión principal de producto. No suele cambiar a lo largo de todo el ciclo de desarrollo salvo que el desarrollo requiera rehacer todo el código desde cero. Evidentemente no se mantiene ninguna compatibilidad con versiones anteriores. </li> <li><strong>Minor Version:</strong> Cambio importante en las interfaces del ensamblado que hace que se rompa la compatibilidad con versiones anteriores. Al igual que la versión principal, este valor se cambia de manera manual. </li> <li><strong>Build Number:</strong> Número calculado automáticamente por el proceso de compilación en orden creciente de manera que dos compilaciones seguidas no generen el mismo valor. Existen muchos esquemas de generación de este valor. Quizá el más conocido es el de basado en el número de días transcurridos desde el 1 de Enero de 2000. </li> <li><strong>Revision:</strong> Número tambien generado automáticamente por el sistema de compilación. Por ejemplo, si se generan varias versiones el mismo día, la versión de Build no cambia, por lo que está va incrementandose de uno en uno por cada compilación. </li> </ul> <h3>AssemblyVersion</h3> <p>Los ensamblados de .NET usan esta versión para enlazarse con otros ensamblados. Esto es, cuando por ejemplo el ensamblado A.dll referencia al ensablado B.dll con version “1.0.0.0”, si sustituimos este último ensamblado por la versión “1.0.0.1”, A.dll no será capaz de encontrarlo pues espera encontrar la versión con la que fue compilado. Por este motivo no se debe dejar a la ligera la opción de Visual Studio de autoincrementar la versión del ensamblado en cada compilación.</p> <p>El significado de los valores de la versión puede ser:</p> <ul> <li><strong>Major Version: </strong>Se actualiza de manera manual. Normalmente significa un cambio radical en la estructura del ensamblado. Por ejemplo porque se ha vuelto a reescribir gran parte del código. Normalmente implica rompe la compatibilidad con versiones anteriores. </li> <li><strong>Minor Version: </strong>Se han añadido nuevas funcionalidades al ensamblado. Y por tanto, puede suponer cambios en la interfaz pero tratando de mantener la compatibilidad con versiones anteriores. Su modificación es manual. </li> <li><strong>Build Number: </strong>Valor calculado automatiamente y que identifica el número de compilación realizado sobre el código fuente. </li> <li><strong>Revision: </strong>Normalmente identifican ligeros cambios sobre el código. Por ejemplo solución de hotfixes. Aquellos ensamblados donde únicamente varíe el número de revisión son totalmente intercambiables entre sí. </li> </ul> <h3>AssemblyInformationalVersion</h3> <p>Este es otro atributo que puede ser usado dentro de AssemblyInfo. Su objetivo es el de exponer una versión del ensamblado a nivel de producto, documentación, instalador, etc. En ningún momento esta información es usada por Windows o por otros ensamblados de .NET.</p> <p>Esta versión se identifica con tres valores:</p> <ul> <li><strong>Major Release: </strong>Normalmente asociada a la versión principal de producto y no suele variar en todo el ciclo de desarrollo del proyecto salvo que se tenga que rehacer todo el contenido del ensamblado.</li> <li><strong>Minor Release: </strong>Este número se asocia a incrementos en la funcionalidad del ensamblado.</li> <li><strong>Maintenance Release: </strong>Los incrementos en este valor suelen reflejar pequeños cambios normalmente asociados a correción de errores, rendimiento o estabilidad. Los clientes del ensamblado no aprecian un cambio en la funcionalidad importada.</li> </ul> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com2tag:blogger.com,1999:blog-1553700282934663138.post-44644145011269541102010-10-22T22:58:00.001+02:002010-10-22T22:59:22.437+02:00Ver un PC virtual con VMWare Player desde la red<p>Hoy ha sido un día un interesante de trabajo para mi compañero Miguel Ángel y para mí. Estamos preparando un sistema de integración continua para nuestro proyecto y nos habíamos decidido por probar la solución de <a href="http://www.jetbrains.com/teamcity/">TeamCity</a>, la cual ofrece una versión gratuita. La idea era montar este servicio sobre una unidad virtual en uno de los ordenadores de la red para que el resto del equipo tuviese acceso también.</p> <p>Creamos la unidad virtual con VMWare Player v3.1.2 con Windows 7 Profesional e instalamos TeamCity sin complicaciones. La nueva unidad virtual puede ver sin problemas el resto de los ordenadores de la red así que configuramos TeamCity para que se conecte a los repositorios de código fuente. Todo perfecto… hasta aquí.</p> <h2><b>El problema</b></h2> <p>Si como he dicho antes la unidad virtual podía ver los ordenadores de la red, no era lo mismo al revés. Nadie, ni tan siquiera el <i>host</i> que albergaba a la unidad virtual era capaz de ver al equipo, y por tanto, acceder al servicio. </p> <p>Buscamos por Internet suponiendo que este problema sería bastante común y que fácilmente encontraríamos la manera de configurar el sistema para solucionarlo. El caso es que no es así, y que además, la manera de solucionarlo es bastante… “truqui del almendruqui”.</p> <h2><b>Buscando la solución</b></h2> <p>Lo primero que debemos hacer es configurar la unidad virtual con configuración de red NAT como se muestra en la imagen:</p> <p><a href="http://lh4.ggpht.com/_oaSweABkmvA/TMH6vY3pBpI/AAAAAAAAANQ/oCwioEVEZPg/s1600-h/clip_image002%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh6.ggpht.com/_oaSweABkmvA/TMH6wAcQSSI/AAAAAAAAANU/j8GwMlsNx1s/clip_image002_thumb%5B1%5D.png?imgmax=800" width="484" height="425" /></a></p> <p>Con esto hacemos que la unidad virtual comparta la misma IP que nuestro equipo <i>host</i>. Pero atención, debemos tener en cuenta que cuando instalamos VMware Player, automáticamente te instala un par de adaptadores de red: VMnet1 y VMnet2. Puedes verlo haciendo una llamada a <i>ipconfig</i> desde la consola:</p> <p><a href="http://lh5.ggpht.com/_oaSweABkmvA/TMH6woFhAaI/AAAAAAAAANY/FD43DrdCLE0/s1600-h/clip_image003%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image003" border="0" alt="clip_image003" src="http://lh6.ggpht.com/_oaSweABkmvA/TMH6xSBnQnI/AAAAAAAAANc/4QuKbjBAfHI/clip_image003_thumb%5B1%5D.png?imgmax=800" width="549" height="563" /></a></p> <p>Para hacer que la unidad virtual utilice de manera correcta el adaptador el adaptador, en nuestro caso VMnet8, necesitamos usar la herramienta de VMware: Virtual Network Editor.</p> <p>Ya te puedes hinchar de buscar que no la vas a encontrar. Parece ser que no viene con la distribución gratuita de VMware Player. ¿O sí?</p> <h3><b>Conseguir Virtual Network Monitor</b></h3> <p>Por lo visto, a los chicos de VMware se les ha ocurrido una curiosa manera de “darte” esta aplicación, pero como si no existiera. En realidad la tienen oculta dentro del fichero de instalación. Para conseguirla tienes que seguir estos pasos:</p> <p>1. Extrae todo el contenido del instalador mediante la llamada: <br />c:\> VMware-player-3.1.2.<versión>.exe /e vmware_temp</p> <p>2. Dentro de la carpeta vmware_temp encontrarás un fichero network.cab. Descomprimelo con WinRAR o 7zip y dentro encontrarás el ejecutable que buscas: vmnetcfg.exe</p> <p>3. Como este ejecutable tiene muchas dependencias con otras librerías, es aconsejable copiarlo a la carpeta donde esté instalado VMware Player.</p> <p>4. Listo para configurar.</p> <h2><b>Configuración con Virtual Network Monitor</b></h2> <p>Una vez arranquemos la herramienta, realizará una búsqueda de todos los adaptadores de redes virtuales que se encuentren en el PC <i>host</i>. Entre ellos encontrará activados VMnet1 y VMnet8. Nosotros nos centramos en VMnet8. El cual configuramos de la siguiente manera:</p> <p><a href="http://lh4.ggpht.com/_oaSweABkmvA/TMH6yvQ8w0I/AAAAAAAAANg/HJ6mUhcFINE/s1600-h/clip_image005%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image005" border="0" alt="clip_image005" src="http://lh5.ggpht.com/_oaSweABkmvA/TMH6zvLhmII/AAAAAAAAANk/GgfCAZ4EGWY/clip_image005_thumb%5B1%5D.png?imgmax=800" width="571" height="480" /></a></p> <p>Debemos asegurarnos que también esté configurado como NAT y que la IP y la máscara de subred corresponden con lo mostrado en la configuración que muestra el <i>host</i> con el ipconfig que le hicimos en VMnet8.</p> <p>Con esto ya hemos conseguido configurar el adaptador de red que está asociado a nuestra unidad virtual.</p> <p>Dado que desde fuera del <i>host</i>, no se tiene acceso a esa subred, y por tanto a la unidad virtual, debemos configurar el NAT para que el host haga de puente entre la red y la unidad virtual. Para ello, asociamos un puerto del <i>host</i> con un puerto de la unidad virtual.</p> <p>Sabiendo que TeamCity lo que expone es un servicio web y por tanto se accede usando el navegador, configuramos el puerto 8080<i> </i>del <i>host</i> para que se correspondiera con el puerto 80 de la unidad virtual:<a href="http://lh6.ggpht.com/_oaSweABkmvA/TMH60H-078I/AAAAAAAAANo/y3-cY58nRnw/s1600-h/clip_image006%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh4.ggpht.com/_oaSweABkmvA/TMH60-S_ZvI/AAAAAAAAANs/HkX7KRgou7U/clip_image006_thumb%5B1%5D.png?imgmax=800" width="511" height="482" /></a></p> <p>Es interesante destacar que hay que hacerlo tanto para el protocolo TCP y UDP, sino, no funciona.</p> <p>Y listo. Cualquier ordenador de la red que acceda desde el navegador a la dirección 192.168.14.248:8080, realmente está accediendo a la subred privada 172.16.37.128:80</p> <p>A continuación la configuración de red de la unidad virtual:</p> <p><a href="http://lh5.ggpht.com/_oaSweABkmvA/TMH61elCEBI/AAAAAAAAANw/onIHUR-rsHk/s1600-h/clip_image008%5B4%5D.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh4.ggpht.com/_oaSweABkmvA/TMH62BKOEQI/AAAAAAAAAN0/4NiY1uYbpMc/clip_image008_thumb%5B1%5D.png?imgmax=800" width="571" height="285" /></a></p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com7tag:blogger.com,1999:blog-1553700282934663138.post-56222629460270775062010-10-03T19:37:00.001+02:002010-10-17T22:36:11.290+02:00El patrón Modelo-Vista-Presentador (MVP) a examen<p>Desde hace tiempo llevo enfrentándome con diversos patrones de diseño cuyo objetivo principal es el de separar la interfaz de usuario de la lógica de las aplicaciones. Desde mis años de estudiante universitario con Smalltalk y el patrón Modelo-Vista-Controlador (MVC), hasta MVVM con WPF, pasando por MVP, MP y sus variaciones.</p> <p>Cada vez que he tenido que me embarcaba a usar uno de estos patrones de diseño me encontraba con la misma curva de aprendizaje. Un concepto bastante sencillo pero que ante ciertos escenarios que encontraba en proyectos reales uno no sabe exactamente qué solución tomar. </p> <p>En esta entrada quiero centrarme en el patrón de diseño Modelo-Vista-Presentador (MVP), pero yendo más allá de la descripción académica de este patrón y enfocándome en cómo puede ser aplicado en aplicaciones reales.</p> <p>Como he comentado antes, MVP es otro patrón de diseño que tiene como objetivo separar la interfaz de usuario de la lógica de las aplicaciones.</p> <p>Básicamente este patrón consiste en 3 componentes:</p> <ul> <li>La vista. Compuesta de las ventanas y controles que forman la interfaz de usuario de la aplicación. </li> <li>El modelo. Que es donde se lleva a cabo toda la lógica de negocio. </li> <li>El presentador. Escucha los eventos que se producen en la vista y ejecuta las acciones necesarias a través del modelo. Además puede tener acceso a las vistas a través de las interfaces que la vista debe implementar. </li> </ul> <p><a href="http://lh3.ggpht.com/_oaSweABkmvA/TKi_PruYxvI/AAAAAAAAAMk/Y4RhDz8q8U4/s1600-h/image%5B28%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_oaSweABkmvA/TKi_QFUwAdI/AAAAAAAAAMo/mQaXlbYiDkc/image_thumb%5B18%5D.png?imgmax=800" width="377" height="211" /></a> </p> <p>El concepto de este patrón es bastante sencillo. Por un lado tengo la vista, que se encarga de mostrar la información al usuario y de interactuar con él para hacer ciertas operaciones. Por otro lado, tenemos el modelo que, ignorante de cómo la información es mostrada al usuario, realiza toda la lógica de las aplicaciones usando las entidades del dominio. Y por último tenemos al presentador que es el que “presenta” a ambos actores sin que haya ningún tipo de dependencia entre ellos.</p> <p>El propósito del presentador tal y como se ha definido en este patrón no establece de manera clara el grado de control que este puede hacer de la vista. </p> <p>Dependiendo de este grado de control podemos encontrarnos variaciones en la manera de ver MVP. De hecho, algunas de ellas, dejan de ser MVP para convertirse en MVC, por lo cual debemos tener cuidado.</p> <h2>MVP como Controlador Supervisado</h2> <p>Por un lado, podemos tener un presentador que no gestione la forma en que la información es mostrada en la vista. Es la vista quien define la lógica de cómo la información es formateada y mostrada en la pantalla a partir de los controles que contiene. En este caso, el presentador únicamente los casos más complejos para facilitar el trabajo de la vista. Martin Fowler llama a esta variación <a href="http://martinfowler.com/eaaDev/SupervisingPresenter.html">Controlador Supervisado</a>.</p> <p><a href="http://lh3.ggpht.com/_oaSweABkmvA/TKi_QfWtIdI/AAAAAAAAAMs/4Yh02736QKE/s1600-h/image%5B29%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_oaSweABkmvA/TKi_Q29s6TI/AAAAAAAAAMw/rxKWna5APp0/image_thumb%5B19%5D.png?imgmax=800" width="514" height="222" /></a> </p> <p>Cuando la vista recibe algún evento de ratón o teclado por parte del usuario, delega el control del evento en el presentador. Este puede realizar ciertas operaciones relacionadas con la vista como el control del estado de los controles y después realizar la llamada a algún comando en el modelo que realice la operación requerida por el usuario. El modelo realiza las operaciones pudiendo realizar cambios en su estado generando el evento correspondiente, el cual es manejado por la vista para actualizar los controles de la pantalla.</p> <p>Hay que tener en cuenta de que el hecho de que la vista pueda hacer referencia al modelo nos da como resultado un diagrama muy parecido al de MVC. Aunque este enfoque nos permita el uso de técnicas de Data Binding. Con lo cual la cantidad de código que la vista y presentador necesitan, se disminuye.</p> <p>Este enfoque nos va a permitir el uso de técnicas de Data Binding sobre el modelo. Por tanto, la cantidad de líneas de código fuente en la vista y el presentador disminuyen. Si nos fijamos en el diagrama, este es similar al de MVC por lo que hay que tener mucho cuidado en no caer en un cambio de patrón.</p> <h2>MVP como Vista Pasiva</h2> <p>Por otro lado, podemos hacer que el presentador gestione totalmente cómo la información se muestra en la vista. Es decir, tenemos una vista "tonta", sin ningún tipo de lógica, cuya única función es la de mostrar la información que se le pasa a través de la interfaz de la vista. Martin Fowler llama a esta variación <a href="http://martinfowler.com/eaaDev/PassiveScreen.html">Vista Pasiva</a>.</p> <p><a href="http://lh6.ggpht.com/_oaSweABkmvA/TKi_RZ800lI/AAAAAAAAAM0/BIJ6gaePaF8/s1600-h/image%5B30%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_oaSweABkmvA/TKi_R-t4quI/AAAAAAAAAM4/WN4O_WRRjVg/image_thumb%5B20%5D.png?imgmax=800" width="404" height="251" /></a> </p> <p>En este caso, cuando un usuario realiza alguna operación sobre la interfaz de usuario, la vista delega los eventos sobre el presentador. Este realizará algún cambio sobre la vista para indicar el cambio de estado y hará las llamadas a los comandos sobre el modelo para llevar a cabo la operación requerida por el usuario. Cuando el modelo provoque cambios en su estado, estos serán recogidos por el presentador (al contrario que en el controlador supervisado, que era la vista quien atendía a estos cambios de estado en el modelo), el cual pedirá al modelo los cambios realizados para luego actualizar la vista acorde a los cambios recibidos.</p> <h2>Ejemplo</h2> <p>Supongamos el siguiente ejemplo: Un programa que para una fecha que le introduzcamos, te diga si es pasado, futuro o la fecha del día actual.</p> <p><a href="http://lh5.ggpht.com/_oaSweABkmvA/TKi_SW3nmlI/AAAAAAAAAM8/ZBox2FFQO7k/s1600-h/image%5B31%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_oaSweABkmvA/TKi_S9JPWTI/AAAAAAAAANA/vykYcay98w4/image_thumb%5B21%5D.png?imgmax=800" width="424" height="239" /></a> </p> <p>Cuando el usuario introduce la fecha y pulsa el botón OK, la vista delega el evento de pulsación de botón sobre el presentador. El presentador toma la fecha introducida del cuadro de texto y valida que el contenido tiene el formato de fecha correcto.</p> <p><i>Nota: He obviado la posibilidad de usar un control específico para introducir fechas y así ver en qué lugar se pude hacer la validación de datos.</i></p> <p>Si la fecha introducida no fuera correcta, el presentador comunicaría al usuario este hecho a través de la vista.</p> <p>En caso de que la fecha fuese correcta, el presentador realizaría la operación de comprobar la fecha usando el modelo. Al recibir el resultado de la operación, el presentador mostraría el resultado a través de la vista. Por ejemplo, modificando el texto de la etiqueta con el resultado.</p> <p>En el siguiente diagrama vemos la secuencia de llamadas realizada para el caso de Vista Pasiva:</p> <p><a href="http://lh3.ggpht.com/_oaSweABkmvA/TKi_TcDBbxI/AAAAAAAAANE/7MesdwCxR5E/s1600-h/image%5B4%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_oaSweABkmvA/TKi_T2Ql9FI/AAAAAAAAANI/NyMd1WQJwm8/image_thumb%5B2%5D.png?imgmax=800" width="550" height="362" /></a> </p> <p>En caso de usar la variación de Controlador Supervisado, supondría que la vista toma el resultado directamente del modelo y que además, la vista define la lógica de cómo el resultado se muestra en la pantalla. En el caso de que la validación de la fecha fallara, el presentador podría, por ejemplo, enviar una excepción hacia la vista que al capturarla decidiría como se mostraría al usuario.</p> <p><em><u>Actualización:</u></em>  <br />He creado en Codeplex un proyecto que hace uso de este patrón. Se trata de un sencillo ejemplo de máquina tragaperras sobre Windows Forms. El enlace al proyecto es <a href="http://mvpslotmachine.codeplex.com/" target="_blank">este</a>.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com22tag:blogger.com,1999:blog-1553700282934663138.post-10229289483133942762010-08-27T13:29:00.001+02:002010-08-27T13:29:42.215+02:00He leído “Dont Make Me Think” de Steve Krug<p>Curioseando en Amazon me encontré con este curioso <a href="http://www.amazon.com/Think-Common-Sense-Approach-Usability/dp/0789723107" target="_blank">libro</a>. Por el título me parecía más bien un libro de esos de autoayuda, pero el caso es que estaba dentro de una colección de libros sobre usabilidad de las interfaces de usuario de las páginas web. Lo que realmente hizo que hiciera clic sobre él fue el número de comentarios, más de 500 y con casi 5 estrellas!! Cuando el resto apenas llegaba a los 100 comentarios.</p> <p><img style="display: inline; margin-left: 0px; margin-right: 0px" align="left" src="http://ecx.images-amazon.com/images/I/4189W8B2NXL._SS400_.jpg" /></p> <p>El libro me lo he leído en un solo día. Son unas 200 páginas y es que el escritor se ha esforzado en que sea ligero y ameno en su lectura: <em>“Si es breve, es más probable que se use”</em></p> <p>El libro trata de los errores más comunes que cometen los desarrolladores y diseñadores cuando abordan (u omiten) el tema de la usabilidad de una página web. Y que al final hacen que un usuario pierda la paciencia o se frustre navegando por sus páginas y termine yéndose a la página de la competencia. </p> <p>Contiene numerosos ejemplos de buenos diseños y malos diseños y de como solucionar los problemas más comunes respecto a su usabilidad. Interesante también el capitulo dedicado a las pruebas de usabilidad. Sin duda para tener en cuenta por cualquier diseñador de interfaces de usuario.</p> <p>Algunas de las claves que da el libro pueden resumirse en:</p> <ul> <li>Los usuarios no quieren pensar mientras navegan por una pagina porque: <ol> <li>No las leen, las escanean buscando palabras clave. </li> <li>No hacen búsquedas optimas, se quedan en el primer enlace que parece que les pueda dirigir a lo que buscan. </li> <li>No averiguan el funcionamiento de las cosas. Se las arreglan con lo que le dice su instinto. </li> </ol> </li> <li>No les importa el número de veces que hay que hacer clic en algo si la opción es mecánica e inequívoca. </li> <li>Elimine la mitad de las palabras en todas las páginas y luego deshágase de la mitad de lo que quede. </li> </ul> <p>Libro muy recomendable para todos aquellos diseñadores de paginas web o que tengan algo que ver con el diseño de Interfaces de Usuario en aplicaciones.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com1tag:blogger.com,1999:blog-1553700282934663138.post-23208627355106136752010-07-11T00:35:00.002+02:002010-07-11T01:01:17.819+02:00Open eBook ReaderMe acabo de estrenar en <a href="http://www.codeplex.com/">CodePlex</a> subiendo un proyecto de mi propia cosecha. Se trata de un lector de libros electrónicos para PC. Lo he llamado Open eBook Reader y lo podéis encontrar en este <a href="http://openbookreader.codeplex.com/">enlace</a>.<br />
<a href="http://lh5.ggpht.com/_oaSweABkmvA/TDj1ycbDuKI/AAAAAAAAAMI/W4fIaGnHGsM/s1600-h/image%5B20%5D.png"><img alt="Open eBook Reader" border="0" height="480" src="http://lh5.ggpht.com/_oaSweABkmvA/TDj1zj5Ga2I/AAAAAAAAAMM/cS-vx-uJ4jU/image_thumb%5B18%5D.png?imgmax=800" style="border-bottom: 0px; border-left: 0px; border-right: 0px; border-top: 0px; display: inline;" title="Open eBook Reader" width="489" /></a> <br />
La verdad es que lo hubiera llamado eBook Reader a secas, pero ya estaba pillado. En fin, que le he acoplado lo del Open delante porque lo he puesto con licencia de Open Source y porque pretendo que únicamente lea formatos de libros que también sean Open Source.<br />
Por ahora sólo lee formato Fiction Book v2.0. Ya iremos ampliando el repertorio de formatos.Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com6tag:blogger.com,1999:blog-1553700282934663138.post-72383633099613838272010-06-08T21:50:00.001+02:002010-06-08T21:50:03.412+02:00S.O.L.I.D. – El Principio Abierto-Cerrado (OCP) (parte 3)<p>En muchas ocasiones hemos visto en nuestro trabajo como nos cambiaban los requerimientos de alguno de los módulos cuando ya estaban prácticamente terminados. Lo que suponía cambiar el módulo, y comprobar que no hubiera efectos colaterales en otros módulos relacionados. Tener en cuenta este principio va a hacer que evitemos muchos de los problemas relacionados con los cambios en un módulo a causa de cambios en las especificaciones de las clases o componentes.</p> <p><a href="http://lh4.ggpht.com/_oaSweABkmvA/TA1xqw7np3I/AAAAAAAAAL4/j2djX6lY6jY/s1600-h/image%5B8%5D.png"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_oaSweABkmvA/TA1xrUbCy5I/AAAAAAAAAL8/PMAL8TxL2wA/image_thumb%5B4%5D.png?imgmax=800" width="355" height="102" /></a></p> <p>Por ejemplo, supongamos una clase conductor (<em>Driver</em>) que es capaz de conducir Vehículos (<em>Vehicle</em>). Si los requerimientos del sistema cambiaran y ahora el conductor tuviese que conducir sólo vehículos a motor, está claro que la clase <em>Vehicle</em> cambiaría en su comportamiento y posiblemente en su interfaz, por lo que la clase <em>Driver</em> tendría que adaptarse a los nuevos cambios del la clase <em>Vehicle</em>. </p> <p>El Principio de Abierto-Cerrado, del inglés “The Open-Close Principle (OCP)”, nos viene a decir que cualquier entidad software (clases, módulos, funciones, etc.) debe de estar abierta para ser extendida en funcionalidad pero cerrada para ser modificada. Es decir, una clase que cumpla con OCP tiene estas dos características:</p> <ol> <li>La funcionalidad del módulo puede cambiarse o extenderse en base a los cambios que requiera el sistema. </li> <li>Extender la funcionalidad de un módulo no implica cambios en el código fuente de ese módulo en si mismo. </li> </ol> <p>¿Cómo se puede entender esto? ¿Cómo cambiar el comportamiento de una clase sin cambiar su código fuente? ¿Cómo hacer que la clase <em>Driver</em> no se tenga que cambiar y sin embargo sea capaz de tratar con el nuevo tipo de vehículo. La respuesta está en la <strong>abstracción</strong>. La abstracción se puede conseguir de muchas maneras en programación orientada a objetos. A través de interfaces, clases abstractas, delegados, etc.</p> <p><a href="http://lh5.ggpht.com/_oaSweABkmvA/TA1xr2DAdtI/AAAAAAAAAMA/ua7XkP8NL9o/s1600-h/image%5B14%5D.png"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_oaSweABkmvA/TA1xsudaIJI/AAAAAAAAAME/IfzNWGCSzVg/image_thumb%5B8%5D.png?imgmax=800" width="353" height="227" /></a> En este case, la interfaz <em>IDriver</em> es la abstracción que define la clase <em>Driver</em> para conducir un <em>Vehicle</em>. Fíjese en que no he llamado a la interfaz <em>IVehicle</em> como podría haber sido lo lógico. Esto es así porque el que define la funcionalidad es el <em>Driver </em>mientras que <em>Vehicle</em> es únicamente una implementación de esa interfaz. Para implementar el cambio que decía que un <em>Driver</em> sólo puede conducir vehículos a motor bastaría con cambiar únicamente la implementación de <em>Vehicle</em> para ajustarse a las nuevas necesidades.</p> <p>Con este diseño, la clase <em>Driver </em>cumpliría con OCP, pues está abierto a cambios (respecto a conducir vehículos) y cerrado a los cambios (para cambiar el comportamiento de los vehículos, no se necesita cambiar). Otro tema sería que los cambios que se pidieran no fuesen soportados por la interfaz disponible. Por ejemplo, tener en cuenta vehículos voladores. En este caso los cambios serían a nivel de requerimientos de más alto nivel, por lo que se entiende que no quede más remedio que cambiar la clase <em>Driver</em>, la interfaz <em>IDriver</em>, y evidentemente todas las implementaciones de esta interfaz.</p> <h4>Anticipación al uso de OCP</h4> <p>No debemos caer en la tentación tampoco de crear abstracciones e interfaces en todas las clases. Esto haría que el diseño creara interfaces por casi cualquier referencia a otra clase que se tuviera. Y haría el código menos legible. Además, esto tampoco nos evitaría tener una clase completamente cerrada. siempre nos pueden pedir un cambio que no esté soportado por las abstracciones que tengamos. Entonces, ¿cuando debemos forzar una clase a que cumpla con este principio? Cuando nos lo diga el sentido común. Yo por ejemplo en mi caso, lo aplico siempre que vea muy claro una dependencia a una clase cuya funcionalidad veo que tenga ciertas posibilidades de que cambie de comportamiento. Otro caso donde es conveniente el uso de OCP es cuando te piden un cambio en una clase, y ese cambio afecta a las clases llamantes de la clase modificada (se arrastran los cambios). En este caso, me creo la interfaz para esas clases y hago que la clase que cambia la implemente. De esta manera, aunque la primera vez tenga que hacer la refactorización de varias clases, me aseguro de que si me piden nuevos cambios, estos sólo afecten a la clase que implemente la interfaz.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com3tag:blogger.com,1999:blog-1553700282934663138.post-42567485454144518822010-06-06T22:42:00.001+02:002010-06-06T22:42:37.441+02:00Nuevo eBook Reader Papyre 6.S Alex<p>Hoy me he llevado una sorpresa. Navegando en busca de tiendas de ebooks me he encontrado que la empresa suministradora en España del lector de ebooks Papyre ha sacado un nuevo modelo al mercado. El Papyre 6.S Alex.</p> <p><img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="Papyre 6.S alex" border="0" alt="Papyre 6.S alex" align="left" src="http://grammata.es/wp-content/uploads/2010/04/ALEX-LIMPIO23-556x1024.jpg" width="261" height="480" /></p> <p>El punto distintivo de este modelo con el resto de la familia de lectores Papyre es la doble pantalla de al que dispone. La típica de 6” para la lectura, y otra más pequeña de 3.5” en color y táctil para los controles. También me ha llamado la atención el que haya cambiado el sistema operativo de Linux por un Android. Y es que parece que este sistema se está colando en todos los dispositivos pequeños.</p> <p>Desde el punto de vista de la conectividad ha dado un paso de gigante con respecto al de sus hermanos pequeños puesto que dispone de conexión WIFI. Así que se podrá navegar por internet, leer el correo, etc.</p> <p>También es capaz de reproducir ficheros MP3 y videos MPEG2/4 por lo que se puede comportar como un dispositivo multimedia. Aunque evidentemente, usándolo así, la batería nos durará mucho menos.</p> <p>También se pueden sincronizar las pantallas. Así que si estamos viendo una página de un periódico o un blog en la mini pantalla, podemos ponerla en la pantalla grande. Eso si, en las 16 escalas de grises que soporta.</p> <p>Y para aquellos que le echen en falta poder navegar usando 3G decir que he leído en algún sitio que tienen preparado una versión de firmware con soporte para esto. Que si todavía no lo han puesto es porque están en negociación con las operadoras de telefonía móvil.</p> <p>Para los valientes, decir que el cacharrito sale por unos 450€ en la página de Grammata. Más información en <a href="http://grammata.es/papyre/papyre-6-s-alex">http://grammata.es/papyre/papyre-6-s-alex</a></p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com2tag:blogger.com,1999:blog-1553700282934663138.post-40726044205922449532010-06-04T01:07:00.001+02:002010-06-04T01:08:59.884+02:00S.O.L.I.D. – El principio de la Responsabilidad Única (SRP) (parte 2)<p>El Principio de Responsabilidad Única (o en inglés Single Responsibility Principle (SRP)) fue descrito por Tom DeMarco y Meilir Page-Jones en un trabajo que llamado “Cohesión” en el que se definen las relaciones de los elementos de un módulo. Este principio nos viene a decir que una clase sólo debería tener una única razón para cambiar.</p> <blockquote> <p>“Una clase debe tener una única razón para cambiar.”</p> </blockquote> <p>Lo que trata de decirnos este principio es que debemos huir de aquellas clases monolíticas que aglutinen varias responsabilidades. Pero, ¿qué es una responsabilidad? Desde el punto de vista de SRP se podría decir que una responsabilidad en una clase es <em>una razón para cambiar esa clase</em>. Es decir, si encontramos que hay más de una razón por la que una clase pueda cambiar entonces es que esa clase tiene más de una responsabilidad.</p> <p>¿Y no sería más sencillo decir que <em>una clase debería tener una sola razón para existir</em> en lugar de para cambiar? Cuidado, porque esto nos podría llevar a hacer muy malos diseños de sistemas. Llevado al pie de la letra podría encontrarme con cientos de clases en mi sistema, cada una con una única función. Lo que haría al sistema nada mantenible.</p> <p>El punto clave que nos dice las razones por la que una clase puede cambiar va a depender del contexto en el que se va a dar uso a esa clase. Pongamos por ejemplo una clase que represente al motor de un coche. ¿Necesitamos conocer el régimen de revoluciones del motor?, ¿el peso?, ¿número de cilindros?, ¿presión del inyector de gasolina?, ¿o lo que nos interesa es simplemente poder arrancarlo y esperar que haga andar a un coche para llevaros de un sitio a otro? La respuesta a estas preguntas va a depender del contexto en el cual usemos la clase motor. No va a tener las mismas necesidades sobre esta clase un fabricante de coches que un usuario que usa el coche para ir de un sitio a otro. El fabricante de coches va a notar un número mayor de responsabilidades en el motor que el usuario del coche. Por tanto, para el fabricante, este principio recomendaría dividir la clase motor en otras más pequeñas que cumplan con las especificaciones de manera individual.</p> <p>Veamos otro ejemplo típico de violación del SRP: Supongamos que tenemos la clase <em>Employee (Empleado)</em> en un sistema de gestión de una empresa cualquiera. Esta clase nos permite realizar las tareas esperadas sobre un empleado: Cargor y almacenarlo en una base de datos, generar la nómina, información básica del empleado, etc.</p> <p><a href="http://lh5.ggpht.com/_oaSweABkmvA/TAg1pe7TxpI/AAAAAAAAALo/-9oHgqxsZxQ/s1600-h/image%5B21%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_oaSweABkmvA/TAg1qF41uHI/AAAAAAAAALs/DA1FP1Q8KBE/image_thumb%5B13%5D.png?imgmax=800" width="620" height="435" /></a> </p> <p>Ahora supongamos dos aplicaciones que hacen uso de la clase <em>Employee. </em>Una para ser usada por el departamento de recursos humanos para la gestión de las nominas del personal y otra para la gestión de los proyectos que lleva la empresa.</p> <p>¿Podemos pensar que no se está siguiendo el SRP? Lo que sería lo mismo, ¿creemos que la clase <em>Employee </em>tiene más de una razón por la que pueda cambiar? A mi se me ocurren unas cuantas: Cambiar el formato de almacenamiento de base de datos, modificar los campos que definen a un empleado, cambiar la lógica de generación de nóminas, etc. Es decir, esta clase tiene varias responsabilidades: Es responsable de la persistencia de los clientes, responsable de caracterizar a un empleado, responsable de generar las nóminas, etc.</p> <p>Las consecuencias de violar el SRP en este caso son dos:</p> <ul> <li>La clase <em>Employee</em> tiene una dependencia con la clase <em>AccountService</em> para poder realizar el cálculo de las nóminas. Por tanto la aplicación de gestión de proyectos, a la hora de hacer el despliegue de esa aplicación, tambien debe incluir la librería que contiene esa clase aunque no la necesite.</li> <li>Si alguna de las aplicaciones necesita implementar nueva funcionalidad y requiere cambiar la definición de la clase <em>Employee</em>, este cambio arrastraría cambios en el resto de aplicaciones que requerirían adaptarse a los nuevos cambios de la clase. En caso de olvidarnos, las consecuencias serían impredecibles.</li> </ul> <p>Una mejora en el diseño sería separar las responsabilidades en clases distintas</p> <p><a href="http://lh4.ggpht.com/_oaSweABkmvA/TAg1qmO1HDI/AAAAAAAAALw/gNmpYOk9Fxo/s1600-h/image%5B38%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_oaSweABkmvA/TAg1rB2RjUI/AAAAAAAAAL0/9_x0uKq0148/image_thumb%5B24%5D.png?imgmax=800" width="642" height="622" /></a> </p> <p>Hemos creado dos nuevas clases: una para la gestión de nóminas y otra para el almacenamiento en la base de datos. Hay que fijarse en el detalle de que la clase <em>Employee</em> no depende de las nuevas clases <em>EmployeeAccount</em> y de <em>EmployeeStorage</em>, sino que la dependencia la tienen las aplicaciones.</p> <p>En definitiva. Este principio es uno de los más simples de SOLID, y sin embargo de los más difíciles de implementar correctamente. Aplicando SRP,podemos alcanzar niveles más bajos de acoplamiento y una cohesión más alta del sistema.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com0tag:blogger.com,1999:blog-1553700282934663138.post-81791108776475692662010-05-29T23:57:00.001+02:002010-05-29T23:57:13.613+02:00S.O.L.I.D. Principios de Diseños Orientados a Objeto (parte 1)<p>En la entrada anterior hablaba de los beneficios de hacer desarrollo software con un nivel bajo de acoplamiento, alta cohesión y una fuerte encapsulación. Cuando nos encontramos con proyectos de pequeño tamaño, estos conceptos más o menos los podemos ir controlando. Pero cuando trabajamos en equipos donde se abarcan proyectos de gran envergadura, es complicado. Sobre todo si los miembros del grupo no tienen claros estos conceptos.</p> <p>En los años 90, Robert C. Martin, hace una recopilación de principios a modo de vía para conseguir que estos desarrollos empresariales alcancen un bajo nivel de acoplamiento, alta cohesión y una fuerte encapsulación. Estos principios se englobaron dentro de las siglas <strong>S.O.L.I.D.</strong></p> <ul> <li><strong>S: </strong>Single Responsibility Principle (SRP) - <em>Principio de Responsabilidad Única</em> </li> <li><strong>O: </strong>Open-Closed Principle (OCP) – <em>Principio de Abierto-Cerrado</em> </li> <li><strong>L:</strong> Liskov Substitution Principle (LSP) – <em>Principio de Substitución de Liskov</em> </li> <li><strong>I:</strong> Interface Segregation Principle (ISP) – <em>Principio de Segregación de Interface</em> </li> <li><strong>D: </strong>Dependency Inversion Principle (DIP) – <em>Principio de Inversión de Dependencia</em> </li> </ul> <p><a href="http://lh5.ggpht.com/_oaSweABkmvA/TAGNtRBqIOI/AAAAAAAAALg/ywvdYifLoZU/s1600-h/image%5B5%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_oaSweABkmvA/TAGNuLg0OtI/AAAAAAAAALk/dW4GTNre5bI/image_thumb%5B3%5D.png?imgmax=800" width="322" height="360" /></a></p> <p>El <strong>Principio de Responsabilidad Única (SRP)</strong> nos dice que una clase sólo debe tener una única razón para cambiar. Este principio nos ayuda a dirigir la cohesión y controlar el acoplamiento.</p> <p>El <strong>Principio de Abierto-Cerrado (OCP)</strong> nos indica cómo diseñar clases de manera que estas se puedan extender sin tener que modificar su funcionalidad principal. Aplicando este principio conseguimos sistemas bien encapsulados y altamente cohesivos.</p> <p>El <strong>Principio de Substitución de Liskov (LSP)</strong> se centra en los conceptos de encapsulación y cohesión. Y dice que una clase que implemente una interfaz o herede de una clase base no debe violar la intención o la semántica de la abstracción heredada.</p> <p>El <strong>Principio de Segregación de la Interfaz (ISP)</strong> nos habla de como las clases no deben exponer interfaces que las aplicaciones que las usan no necesitan. Aplicando este principio se obtiene una mejora en la encapsulación y cohesión de sistemas.</p> <p>El <strong>Principio de Inversión de Dependencias (DIP) </strong>está enfocado en hacernos entender de como los detalles de implementación debe depender de la funcionalidad de alto nivel que requiere el sistema y no al revés. Y de como conseguir invertir esta relación de manera satisfactoria.</p> <p>Aunque todos los principios SOLID están relacionados entre sí.Voy a dedicar una entrada a profundizar sobre cada uno de estos dando ejemplos muy básicos y explicativos que ayuden a entender la manera en que pueden ser usados en nuestros desarrollos.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com0tag:blogger.com,1999:blog-1553700282934663138.post-68730262424495216382010-04-22T00:38:00.001+02:002010-05-05T23:28:31.979+02:00Principios básicos de la Orientación a Objetos<p>A los que sobre todo han ido a la Universidad a estudiar asignaturas de programación con orientación a objetos les sonarán los conceptos de acoplamiento, cohesión y encapsulación. Son conceptos muy fáciles de entender, pero que normalmente muchos desarrolladores no aplican a sus proyectos. Muy probablemente se deba a que estos desarrolladores, a pesar de conocer estos conceptos, no entienden los beneficios de usar diseños con bajos niveles de acoplamiento, alta cohesión y una buena encapsulación de los componentes.</p> <p>En esta entrada quiero mostrar las ventajas de usar correctamente estos principios y como nos podría ayudar en os desarrollos que estemos haciendo.</p> <h2>Acoplamiento</h2> <p>En desarrollo software, el acoplamiento se puede definir como el grado en el que dos ó más componentes o clases dependen unas de otras.</p> <p>Un buen diseño o una buena arquitectura tratará de mantener un nivel de acoplamiento bajo. Me explico con un ejemplo: supongamos que tenemos un software con la siguiente arquitectura de componentes donde se muestran las dependencias que hay entre estos: <a href="http://lh3.ggpht.com/_oaSweABkmvA/S89-XLO_FpI/AAAAAAAAALI/baOhr8SvoTQ/s1600-h/Acoplamiento.png"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="Acoplamiento" border="0" alt="Acoplamiento" src="http://lh3.ggpht.com/_oaSweABkmvA/S89-X_SMW3I/AAAAAAAAALM/mwuVOSh4ip0/Acoplamiento_thumb.png?imgmax=800" width="297" height="164" /></a> Ahora tenemos otro desarrollo distinto, y vemos que una de las funcionalidades ya la realiza el Módulo C del proyecto de la imagen. Perfecto, cogemos ese módulo y lo usamos en nuestro nuevo desarrollo. En seguida nos damos cuenta que es más fácil decirlo que hacerlo. Tiene demasiadas dependencias con otros componentes que a su vez también tienen otras dependencias. Por lo que usar el componente C nos va a suponer también traernos muchos de los otros componentes de los que depende. Es decir, el componente C tiene un grado de acoplamiento muy alto con el resto de componentes.</p> <blockquote> <p>“Si un componente tiene un alto grado de acoplamiento con otros componentes, entonces para usar ese componente vas a tener que usar también los otros de los que dependen aunque no los necesites.”</p> </blockquote> <p>Se puede reducir el nivel de acoplamiento de un componente de muchas maneras. La más usual es la de usar interfaces o herencia de clases entre los componentes que desacoplen la dependencia entre estos.</p> <p>Por el lado contrario, no existen los componentes con nivel de acoplamiento cero. No tienen sentido. Un módulo que no depende de nada y del que nadie depende, ¿qué uso puede tener?</p> <p>En definitiva, el acoplamiento no es algo estrictamente malo. Los componentes que forman parte de un sistema deben de poder interactuar unos con otros para que este pueda realizar alguna función. Por tanto, el objetivo es alcanzar un equilibrio en el grado de acoplamiento de los componentes de manera que sea funcional (que haga lo que tiene que hacer), entendible y mantenible por otros desarrolladores.</p> <h2>Cohesión</h2> <p>El grado de cohesión de un componente es la relación funcional que existe entre las funciones de ese componente para realizar una tarea. Es decir, un módulo <em>coherente</em> es capaz de realizar una tarea sin necesidad de interactuar con otros componentes.</p> <p>Haciendo una extrapolación para sistemas complejos, la cohesión define cómo los distintos componentes que forman parte del sistema se relacionan e interaccionan para crear un sistema que da un valor añadido mayor que el que pueden dar la suma de sus partes.</p> <blockquote> <p>“El todo es mayor que la suma de las partes.”</p> </blockquote> <p>Este es un concepto mucho más abstracto y quizás más difícil de conseguir debido a que no hay modelos ni recetas mágicas que nos digan como conseguir desarrollar software con un alto grado de cohesión. Sin embargo es un concepto que es bastante usado de la vida cotidiana. Muchas veces hemos oído aplicarlo al deporte: Equipos con alto valor de cohesión consiguen mejores resultados en las competiciones en las que participan que otros menos cohesionados aunque sus jugadores sean mucho mejores. También las empresas buscan esa cohesión: tener equipos de trabajoque trabajen juntos para conseguir productos a tiempo con la mejor calidad posible.</p> <p>Supongamos la arquitectura del sistema de la imagen siguiente.<a href="http://lh4.ggpht.com/_oaSweABkmvA/S89-YYI3RjI/AAAAAAAAALQ/qgs0xO3nsII/s1600-h/Cohesion%5B9%5D.png"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="Cohesion" border="0" alt="Cohesion" src="http://lh6.ggpht.com/_oaSweABkmvA/S89-ZCTazbI/AAAAAAAAALU/OjUmEKmRl4s/Cohesion_thumb%5B7%5D.png?imgmax=800" width="488" height="368" /></a></p> <p></p> <p>Si se analizan por separado cada uno de los componentes del sistema realmente no se tiene gran cosa. Por separado cada componente realiza una serie de tareas muy limitadas con poco valor por si mismo. Sin embargo, cuando se juntan los componentes y se hace que interaccionen coherentemente entre sí, el valor aportado es muchísimo mayor.</p> <p>Para conseguir hacer software con un nivel de cohesión elevado debemos tratar de huir de las mega-clases y mega-componentes que hacen de todo. Es posible que internamente estén bien cohesionados, pero a nivel de sistema no será así. Por ejemplo, que un componente sea capaz de leer los datos de la base de datos, procesarlos y mostrarlos en algún tipo de interfaz de usuario.</p> <p>Hay muchos patrones que están enfocados a lograr buenos niveles de cohesión. Por ejemplo el multi-capa (como el del diagrama anterior), MVC, MVVM, etc.</p> <h2>Encapsulación</h2> <p>El grado de encapsulación de un componente viene definido por la forma en el que el componente oculta la información superflua y la lógica de lo que hace al resto de componentes del sistema.</p> <p>Y es que muchos desarrolladores entiende por encapsulación sólo la ocultación de información, dejando de lado la lógica que los componentes implementan. <br /><a href="http://lh3.ggpht.com/_oaSweABkmvA/S89-ZpnRLAI/AAAAAAAAALY/MSX0Rma5oD0/s1600-h/Encapsulaci%C3%B3n%5B13%5D.png"><img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="Encapsulación" border="0" alt="Encapsulación" src="http://lh3.ggpht.com/_oaSweABkmvA/S89-af8a3EI/AAAAAAAAALc/Zvggm4DkSl0/Encapsulaci%C3%B3n_thumb%5B11%5D.png?imgmax=800" width="303" height="181" /></a></p> <p>Supongamos un diseño en el que la clase X está bien encapsulado. Entre otras ventajas, el hecho de ocultar la lógica hacia el exterior hace que podamos cambiar la implementación interna sin que afecte al resto de clases del diseño. Por otro lado, si hacemos que la funcionalidad de la clase X se base en la implementación de interfaces, los componentes que necesiten usar X se referirán a ella por sus interfaces y no por la clase en sí.</p> <p>Por ejemplo, supongamos este par de interfaces sencillas definidas en C#: </p> <pre class="brush: csharp; ruler: true;">public interface IAnimal<br />{<br /> string Especie { get; }<br /> void Comer();<br /> void Andar();<br /> void Atacar();<br />}<br /><br />public interface IPersona<br />{<br /> string Nombre { get; set; }<br /> DateTime FechaNacimiento { get; set; }<br /> void Trabajar();<br /> void Hablar();<br />}<br /><br />public class HombreLobo : IAnimal, IPersona<br />{<br /> #region IAnimal Members<br /><br /> public string Especie { get { return "Lobo"; } }<br /><br /> public void Comer()<br /> {<br /> // ...<br /> }<br /><br /> public void Andar()<br /> {<br /> // ...<br /> }<br /><br /> public void Atacar()<br /> {<br /> // ...<br /> }<br /><br /> #endregion<br /><br /> #region IPersona Members<br /><br /> public string Nombre {get; set;}<br /> <br /> public DateTime FechaNacimiento {get; set;}<br /> <br /> public void Trabajar()<br /> {<br /> // ...<br /> }<br /><br /> public void Hablar()<br /> {<br /> // ...<br /> }<br /><br /> #endregion<br />}</pre><br /><br /><p>Los componentes que quieran usar esta clase lo pueden hacer basándose en las interfaces que la clase implementa:</p><br /><br /><pre class="brush: csharp; ruler: true;">public class MiClase<br />{<br /> public void HacerAlgo()<br /> {<br /> IAnimal lobo = new HombreLobo();<br /> lobo.Atacar();<br /> lobo.Comer();<br /> }<br />}<br /></pre><br /><br /><p>Una correcta encapsulación de las clases o componentes nos ayuda a reducir la duplicación de datos y de procesos en nuestros desarrollos y nos va a ayudar en situaciones donde necesitemos usar alguna funcionalidad proporcionada por alguna de nuestras clases en más de un sitio.</p><br /><br /><h2>Para terminar</h2><br /><br /><p>Cada uno de los principios que hemos visto están conectados entre sí: El desarrollo de componentes fuertemente encapsulados facilita que consigamos disminuir el nivel de acoplamiento de estos componentes. Y por consecuencia, tener encapsulación y bajo acoplamiento nos ayudará a asegurarnos el tener un sistema altamente cohesionado.</p> Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com0tag:blogger.com,1999:blog-1553700282934663138.post-37727446902523984472010-04-18T02:02:00.000+02:002010-04-18T02:02:15.475+02:00Windows 7. ¿32 o 64 bits?Estoy pendiente de formatear mi PC y no tengo claro si poner la versión de Windows 7 de 32 ó la de 64 bits. O lo que es lo mismo, Windows x86 ó x64. Así que me he lanzado a la tarea de ver las diferencias que hay entre ambas versiones.<br />
<br />
Haciendo un poco de historia, las versiones de sistemas operativos de 64 bits empezaron a popularizarse con la salida de Windows XP de 64 bits, que coincidió con la escalada tecnológica de AMD e Intel por sacar procesadores de 64 bits.<br />
La historia ha dejado claro que el mercado no estaba preparado para sistemas operativos de 64 bits. Windows XP 64 bits no tuvo buena acogida. Además del pobre rendimiento que daba en algunas aplicaciones de 32 bits, tenía el handicap de que necesitaba drivers de dispositivos nativos de 64 bits.<br />
No ha sido hasta Windows 7 que Microsoft se ha centrado en hacer un sistema operativo realmente enfocado para procesadores de 64 bits unido con la gran cantidad de drivers que traía y el comienzo de los fabricantes de dispositivos a entregar controladores nativos de 64 bits cuando Windows x64 se ha convertido en una opción real para los consumidores.<br />
<br />
La principal diferencia entre cualquier sistema operativo de 64 bits y otro de 32 bits es la cantidad de memoria RAM que puede manejar. La CPU de un ordenador de 32 bits puede direccionar 2 elevado a 32 posiciones de memoria. O lo que es lo mismo, 4 gigas de RAM, que es lo máximo que normalmente tenemos en nuestros equipos. Sin embargo, con una CPU de 64 bits, la cantidad de memoria que se puede manejar se multiplica. La limitación de la cantidad de RAM que se puede poner en un PC no la pone la CPU, sino el sistema operativo o la placa base del PC.<br />
Si no me equivoco, Windows 7 x64 es capaz de soportar hasta 192 gigas de RAM, que es bastante más de lo que cualquier placa base soporta. Esto repercute en el rendimiento del sistema operativo, que es capaz de manejar más aplicaciones a la vez, y cambiar entre aplicaciones más rápidamente.<br />
<br />
En definitiva. ¿Qué hace que me decante por instalar la versión de 64 bits en lugar de la de 32?<br />
Bien, a parte de tener una CPU de 64 bits, lo principal es asegurarse de que disponemos de versiones de controladores para nuestros dispositivos que sean nativos de 64 bits.<br />
Con respecto a los programas. Según Microsoft no hay problema en ejecutar software de 32 bits salvo algunos antivirus y por supuesto los controladores de dispositivos.<br />
<br />
Así que por mi parte ya lo tengo claro. En cuento encuentre controladores de 64 bits le instalo el Windows 7 x64.<br />
<br />
Saludos a todos.Anonymoushttp://www.blogger.com/profile/09715126748303335048noreply@blogger.com1