Sie sind auf Seite 1von 21

Programación Orientada a Objetos en

Java
En nuestro post de hoy vamos a tratar el tema la programación orientada a
objetos en Java, ¡no todo va a ser .NET!

En si la POO es bastante amplia y muy necesario para el desarrollo de


software pero ahora nos concentraremos en hacer un ejemplo básico con el
cual se pueda entender la forma en que funciona esto.

Lo que incluye la POO es la Herencia, Polimorfismo, Encapsulamiento, etc.


claro que los 3 mencionados es la parte clave de esto, aunque también
podríamos hablar de cohesión, abstracción, ocultamiento y otros.

Primero veremos un poco de teoría para entenderlo de mejor forma.

Abstracción:
Abstracción en la POO se refiere a poder identificar el posible
comportamiento de un objeto para posteriormente convertirlo en sus métodos
y funciones dentro de una clase.

Encapsulamiento:
Consiste en reunir todos los elementos de un posible objeto el cual se
verán como atributos del mismo para luego realizar el tratado de sus datos
de mejor forma.

Imaginen la clase Persona y un objeto de esta, por ejemplo “Carlos” el


encapsulamiento respectivo a este debería ser sus atributos como nombre,
apellidoPaterno, apellidoMaterno, Edad, etc.

Modularidad:
Es la separación de un software por módulos, en si es una característica
muy importante de la POO. Esta es una forma de realizar software con
módulos independientes pero escalables y que se puedan extender y realizar
el mantenimiento previo de una forma muy sencilla.

Ocultación:
Esto consiste en separar el acceso directo a un objeto, entonces diríamos
que se puede acceder a tales objetos sólo mediante una capa especificada
por este mismo.

Polimorfismo:
Es la acción de poder usar un múltiples posibilidades en una misma
propiedad. También podríamos decir que a nivel de programación sería usar
un método, función, entre otros llamados de la misma forma pero con
diferentes propósitos.

Herencia:
En la POO las clases no se encuentran aisladas, si no que pueden
interactuar con otras clases así compartiendo sus atributos y métodos que
puedan existir entre estas. Un ejemplo sería una clase Persona la cual es
heredada a la clase PersonaNatural y PersonaJurídica. Persona tendría los
datos comunes entre ambos.

Ejemplo

Para este ejemplo sólo veremos un poco de Encapsulamiento, Polimorfismo y


Herencia, también se podrá ver algo de ocultamiento con la capa de negocio
implementada.

– Creamos el proyecto con sus respectivas clases y formularios.

– Ahora diseñamos los formularios “frmPersona”, “frmPersonaNatural” y


“frmPersonaJuridica” respectivamente las cuales se verán de la siguiente
forma.
El primer formulario “frmPersona” es el que se heredará a los otros dos
formularios por lo que tiene los datos en común de ambos. Recordar que
cuando se crea una clase superior que se quiere heredar a otras, se debe
cambiar a todos sus métodos y componentes que se quiere acceder desde la
clase que lo hereda a protected para que esta pueda manipular sus
componentes y métodos o public para que puedan ser accedidos desde
cualquier clase. Por lo tanto tenemos que cambiar los dos componentes
JTextField y el JTable de private a protected en el menu
de propiedades->Code->Variable Modifiers ya que sólo queremos que estos
componentes puedan ser accedidos sólo desde su clase que lo hereda.

– Ahora veamos que para heredar por ejemplo en


el formulario “frmPersonaNatural” elformulario “frmPersona” sólo se agrega
en la creación de la clase “extends frmPersona” donde “frmPersona” es el
nombre de la clase de donde heredamos. En este caso extends ya existe
heredando de Swing el cual borramos y cambiamos por nuestroformulario. La
línea donde se crea la clase debería quedar de la siguiente forma.

public class frmPersonaNatural extends frmPersona {


.
.
.

Para el otro formularios sería de la misma forma.

– Realizado esto, al compilar nuestro formulario “frmPersonaNatural”


debería verse de la siguiente forma.

El formulario “frmPersonaJuridica” se verá de la misma forma con la


diferencia de que DNI será RUC.

– Código de la clase de encapsulado TPersona

package Encapsulamiento;

/**
*
* @author kaaf
*/
public abstract class TPersona {
protected String nombreCompleto;
protected int edad;

public void NombreCompleto(String nombreCompleto)


{
this.nombreCompleto=nombreCompleto;
}
public String NombreCompleto()
{
return this.nombreCompleto;
}
public void Edad(int edad)
{
this.edad=edad;
}
public int Edad()
{
return this.edad;
}
}

Este es nuestro encapsulado que se hereda a los otros dos. Como se puede
ver la clase conlleva un método “abstract” el cual hace que la clase sólo
pueda heredarse a otras clases mas no pueda invocarse (No se puede invocar
en clases que no lo heredan). También vemos atributos de tipo protected,
esto hace que dichas variables puedan ser accedidos solamente desde la
clase que lo hereda.
– Código de la clase de encapsulamiento TPersonaNatural.

package Encapsulamiento;

/**
*
* @author kaaf
*/
public class TPersonaNatural extends TPersona{
private String dNI;

public TPersonaNatural(){}

public TPersonaNatural(String nombreCompleto, int edad, String dNI)


{
this.nombreCompleto=nombreCompleto;
this.edad=edad;
this.dNI=dNI;
}

public void DNI(String dNI)


{
this.dNI=dNI;
}
public String DNI()
{
return this.dNI;
}
}
Aquí vemos que es algo más cotidiano, con la diferencia de que heredamos de
TPersona y así podemos acceder a sus atributos “nombreCompleto” y “edad” ya
que estos son protected y están siendo accedidos mediante la herencia.
– Código de la clase de encapsulamiento TPersonaJuridica.
package Encapsulamiento;

/**
*
* @author kaaf
*/
public class TPersonaJuridica extends TPersona {
private String rUC;

public TPersonaJuridica(){}

public TPersonaJuridica(String nombreCompleto, int edad, String rUC)


{
this.nombreCompleto=nombreCompleto;
this.edad=edad;
this.rUC=rUC;
}

public void RUC(String rUC)


{
this.rUC=rUC;
}
public String RUC()
{
return this.rUC;
}
}

Se hace lo mismo que en TPersonaNatural.


Como podemos ver en ambos casos tenemos constructores que cumplen
diferentes funciones. El primer constructor de ambos casos sólo sirve para
instanciar a la clase de encapsulado y el segundo constructor instancia con
el paso de atributos para su almacén dentro del encapsulado. A este acto se
le puede considerar “polimorfismo”.
– Código de la clase NegocioPrincipal
package Negocio;

import Encapsulamiento.TPersonaJuridica;
import Encapsulamiento.TPersonaNatural;

/**
*
* @author kaaf
*/
public class NegocioPrincipal {

public static TPersonaNatural CargarEncapsuladoPN(String


nombreCompleto, int edad, String dNI)
{
return new TPersonaNatural(nombreCompleto, edad, dNI);
}

public static TPersonaJuridica CargarEncapsuladoPJ(String


nombreCompleto, int edad, String rUC)
{
TPersonaJuridica tPersonaJuridica=new TPersonaJuridica();
tPersonaJuridica.NombreCompleto(nombreCompleto);
tPersonaJuridica.Edad(edad);
tPersonaJuridica.RUC(rUC);
return tPersonaJuridica;
}
}

Como podemos ver tenemos dos métodos de tipo public lo que permite acceder
desde cualquier lado y static que libera memoria después de usar dicho
método.
En el primer método vemos el funcionamiento de uno de los constructores de
la clase y en el segundo se muestra el otro constructor de sólo
instanciación.
– Código del botón Registrar de TPersonaNatural

private void btnRegistrarActionPerformed(java.awt.event.ActionEvent evt) {

TPersonaNatural
tPersonaNatural=NegocioPrincipal.CargarEncapsuladoPN(txtNombreCompleto.get
Text(), Integer.parseInt(txtEdad.getText()), txtDNI.getText());
Object[] filaNueva=
{
tPersonaNatural.NombreCompleto(),
tPersonaNatural.Edad(),
tPersonaNatural.DNI()
};

DefaultTableModel
modeloTabla=(DefaultTableModel)TablaPersona.getModel();
modeloTabla.addRow(filaNueva);

TablaPersona.setModel(modeloTabla);
}

– Código del botón Registrar de TPersonaJuridica

private void btnRegistrarActionPerformed(java.awt.event.ActionEvent evt) {

TPersonaJuridica
tPersonaJuridica=NegocioPrincipal.CargarEncapsuladoPJ(txtNombreCompleto.ge
tText(), Integer.parseInt(txtEdad.getText()), txtRUC.getText());
Object[] filaNueva=
{
tPersonaJuridica.NombreCompleto(),
tPersonaJuridica.Edad(),
tPersonaJuridica.RUC()
};

DefaultTableModel
modeloTabla=(DefaultTableModel)TablaPersona.getModel();
modeloTabla.addRow(filaNueva);

TablaPersona.setModel(modeloTabla);
}

Realizado todos estos pasos, nuestra aplicación debería correr de la


siguiente forma.
Ojo: No olviden cambiar los componentes y métodos de las clases superiores
de private a protected o public según lo requiera, caso contrario al
heredar no se podrá tener acceso a los componentes de la clase superior.
Pudimos ver que la POO no es nada complejo sólo que es un poco trabajoso y
aún más si lo implementamos junto a la Programación en Capas, pero tener en
cuenta que si se usan estas tecnologías el desarrollo podrá ser más
trabajoso pero el mantenimiento y la extensión del software es mucho más
sencillo y escalable.

www.asociacionaepi.es

Las mejoras de seguridad en Android


4.4
Todos sabemos que Android 4.4, viene cargada de novedades, muchas de ellas visibles
pero hay otras que vienen en su interior que mejoran para proteger al usuario, os
hablamos de los mejoras de seguridad.

Estas mejoras corrigen los bugs detectados en anteriores informes de seguridad,


incluso mejoran el sistema para complicar la introducción a las distintas capas de
seguridad. Os describimos los siguientes:

SELinux en Enforce Mode


SELinux fue introducido en la versión 4.3 como un sistema de control para acceder al
núcleo de linux para evitar ataques de escalado de privilegios, este sistema esta en
modo permisivo y se cambia en esta nueva versión a enforcing mode.

Soporte de claves de cifrado de curva elíptica

(ECDSA) en

AndroidKeyStore
Aunque este tipo de cifrado ha sido criticado, se incluye en el almacén de contraseñas
de Android, dando soporte para claves de firma de Curva Elíptica. Es una alternativa
para los desarrolladores pero la sigue siendo la mejor opción para un almacenamiento
de datos a largo plazo la selección de cifrado simétrico.

Avisos de Cifrados sospechosos SSL CA


Android 4.4 incluye una clave CA adicional (Certificado de Autoría), que nos avisará
si nuestro dispositivo recibe algún certificado SSL CA sospechoso.
Detección automática de desbordamiento de pila
La nueva versión de Android esta compilado FORTIFY_SOURCE a nivel 2, lo que nos
asegura que todo el código esta compilado con esta protección. Dicha característica
del compilador identifica oportunidades de overflow que pueden ser explotadas por
cualquier aplicación, este sistema no elimina esos agujeros pero seguramente en la
próxima versión solucionen este tipo de agujeros. Creo que mas bien quieren recoger un
informe de errores para poder encontrar una solución mas adelante. Algo es algo.

Google Certificate Pinning


Este certificado nos permite ejecutar certificados SSL que serán recogidos en una
lista de manera de los que no estuvieran denegarían ese acceso. Este sistema evita
ataques de tipo man-in-the-middle a la hora de aceptar un certificado válido SSL CA,
dotando así de una doble seguridad haciendo más difícil la tarea.

www.asociacionaepi.es

Delegados en C#
Lo primero que te preguntas es que tipo de problema resuelven los delegados,
o sea… para que debería aprender a usar delegados? Para responderte esta
pregunta, lo mejor es ilustrarlo con un ejemplo:

Supongamos que tenemos el siguiente método de ordenación.

public void Sort(IComparable[] items){


for(i=0; i<items.length; i++)
{
Icomparable tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}

El método Sort, nos permitirá ordenar cualquier cosa, que herede de la


interfaz IComparable y que implemente el método CompareTo,

Por ejemplo, supongamos que tenemos la clase Persona definida de la siguiente


forma:
//Definimos la clase persona
public class Persona: IComparable
{
//Aqui usamos propiedades autoimplementadas (a partir de C# 3.0)
//Fijate que no hay que definir primero las variables privadas
public string Name { get; set; }

public Date Birthday { get; set; }

public float Height { get; set; }

public Persona(string Name, Date Birthday, float Height)


{
this.Name = Name;
this.Birthday = Birthday;
this.Height = Height;
}

//Metodo CompareTo de la interfaz IComparable


public int CompareTo(object obj)
{
Persona p = obj as Persona;
//Comparamos las personas por Fecha de Nacimiento
return this.Birthday.CompareTo(p.Birthday);
}
}
//Clase Date que utilizamos en el campo Birthday de la clase Persona
public class Date : IComparable
{
//Aqui usamos variables porque las propiedades Day, Month y Year
//no tienen parte set
int day, month, year;

//Constructor de la clase
public Date(int day, int month, int year)
{
this.day = day;
this.month = month;
this.year = year;
if (!IsValid) throw new ArgumentException("Fecha no válida");
}

//Metodo para verificar si una fecha es valida


bool IsValid
{
get{
//Verifico si la fecha tiene rango valido
if (year &lt;= 0 || month &lt;= 0 || month &gt; 12 || day &lt;= 0 || day
&gt; 31)
return false;
//Verifico si el año es bisiesto que febrero no tenga mas de 28 dias
if ((year % 400 == 0 && month == 2 && day > 28) || (month == 2 && day >
29))
return false;
// Verifico los meses que no tienen 31 dias
if ((month == 4 || month == 6 || month == 9 || month == 11) &amp;&amp; day
== 31)
return false;
return true;
}
}
public int Day
{
get { return day; }
}
public int Month
{
get { return month; }
}
public int Year
{
get { return year; }
}

//Metodo para comparar fecha, nota que esta clase tambien hereda
//de IComparable
public int CompareTo(object x)
{
Date d = x as Date;
if (d == null)
throw new ArgumentException();
// Uso operadores ternarios para comparar las fechas (asi me evito unos
cuantos if else)
return (d.Year > year || (d.Year == year && d.Month > month) || (d.Year ==
year && d.Month == month && d.Day > day)) ? -1 : (d.Year == year && d.Month
== month && d.Day == day) ? 0 : 1;
}
}

class Program
{
//En el metodo Main es donde usamos las clases y
//programamos algunos metodos que vayamos a utilizar

static void Main(string[] args)


{
//Creo el array de personas que voy a ordenar
Persona[] personas = new Persona[]
{
new Persona("Tom", new Date(1, 11, 1988), 169),
new Persona("Jio", new Date(31, 8, 1990), 170),
new Persona("Simone", new Date(7, 9, 1991), 165),
new Persona("Ruben", new Date(22, 10, 1988), 165)
};

//Ordeno el array de personas


Sort(personas);

//Imprimo las personas despues de ordenarlas


foreach (Persona p in personas)
{
Console.WriteLine("Nombre: {0} Date: {1}, {2}, {3}, Height: {4}",
p.Name, p.Birthday.Day, p.Birthday.Month, p.Birthday.Year, p.Height);
}
Console.ReadKey();
}

//Metodo de ordenacion sencillo


//Puedes ver otros métodos de ordenación en post anteriores:

static void Sort(IComparable[] items)


{
for (int i = 0; i < items.Length - 1; i++)
for (int j = i + 1; j < items.Length; j++)
//Comparamos las personas usando el metodo CompareTo que implementamos
if (items[i].CompareTo(items[j]) > 0){
IComparable tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}
}

Como has visto hasta ahora, el método Sort se basa solo en el criterio
definido por el método CompareTo de la clase que implemente IComparable (en
este caso la clase Persona), pero que tal si quisiéramos tener diferentes
criterios de ordenación para un mismo objeto? En nuestro caso, que tal si
quisieramos ordenar un grupo de personas por orden alfabético? o por
estatura?

Una alternativa

Para lograr esto se podría pensar en tener varios métodos de comparación


dentro de la clase Persona, pero entonces ya el método Sort no nos serviría
para comparar cualquier tipo que fuera IComparable, sino que la signatura
sería static voidSort(Persona[] items) y por tanto tendríamos que escribir un
método Sort para cada tipo de objeto que vayamos a ordenar.

El código cliente (el que está en el método Main) debería tener la


posibilidad de escoger el criterio de comparación que desea utilizar, y es
aquí donde el uso de delegados nos viene como anillo al dedo.

La solución usando delegados

Un delegado no es más que un tipo que recibe métodos con una signatura
específica, al principo va a resultar un poco chocante si nunca te has visto
en la necesidad de usarlo, porque no es muy común y muchos lenguajes no lo
soportan, y la verdad a mi me tomó un poco de tiempo asimilar esto, así que
si todavía no logras entender bien, te aconsejo que busques un poco más de
información en el MSDN.

La signatura de los delegados viene siendo de la siguiente forma:

public delegate [valor de retorno] Comparison([parametro 1], [parametro 2],


... , [parametro n]);

En nuestro caso el delegado que vamos a utilizar es:


public delegate int Comparison(object x, object y);

que devuelve un entero y recibe dos objetos, esto quiere decir que este
delegado recibirá como argumento cualquier método que devuelva un int y
reciba dos parámetros de tipo object. A mi no me gusta mucho
utlizar object (ya que tenemos genericidad) pero es mejor en este caso para
ilustrar su utilidad.

Otra característica de los delegados es que lo podemos definir en cualquier


parte del código, o sea, no tiene que estar dentro de una clase como los
métodos, variables o propiedades, de hecho no tengo ni idea de como está
implementado este tipo, pero apuesten a que voy a buscar después que termine
este post…

Una vez que insertemos el delegado en cualquier parte de nuestro código,


modificamos el método Sort para que lo use:

static void Sort(Persona[] items, Comparison compare)


{
for (int i = 0; i < items.Length - 1; i++)
for (int j = i + 1; j < items.Length; j++)
//Comparamos usando el delegado
if (compare(items[i],items[j]) > 0)
{
Persona tmp = items[i];
items[i] = items[j];
items[j] = tmp;
}
}

Añadimos los métodos para comparar por nombre, por fecha de nacimiento y por
altura a la clase Persona:

//Comparamos por nombre en orden alfabético


public static int CompareByName(object x, object y)
{
Persona p1 = x as Persona;
Persona p2 = y as Persona;
//En C# se considera que una letra minuscula es menor que cualquier letra
mayuscula
//por eso tenemos que usar la propidad OrdinalIgnoreCase de la clase
StringComparer
return StringComparer.OrdinalIgnoreCase.Compare(p1.Name, p2.Name);
}

//Comparamos por altura de menor a mayor


public static int CompareByHeight(object x, object y)
{
Persona p1 = x as Persona;
Persona p2 = y as Persona;
//El tipo float tiene un comparador por defecto, que es el que utilizamos
return p1.Height.CompareTo(p2.Height);
}

//Comparamos por fecha de nacimiento como hacíamos en CompareTo


public static int CompareByBirthday(object x, object y)
{
Persona p1 = x as Persona;
Persona p2 = y as Persona;
//El tipo float tiene un comparador por defecto, que es el que utilizamos
return p1.Birthday.CompareTo(p2.Birthday);
}

Ahora veamos como utlizar estos en el código cliente:

static void Main(string[] args)


{
Persona[] personas = new Persona[]
{
new Persona("Tom", new Date(1, 11, 1988), 169),
new Persona("Jio", new Date(31, 8, 1990), 170),
new Persona("Simone", new Date(7, 9, 1991), 165),
new Persona("Ruben", new Date(22, 10, 1988), 165)
};

//Pasamos como parámetro al delegado el método CompareByName


Sort(personas, Persona.CompareByName);
//También se puede usar el nombre del método directamente sin pasarlo como
parámetro
//Sort(personas, new Comparison(Persona.CompareByName));

Console.WriteLine("Ordenando las personas en orden alfabético");


foreach (Persona p in personas)
{
Console.WriteLine("Nombre: {0}", p.Name);
}

Console.WriteLine();
Console.WriteLine("Ordenando las personas por fecha de nacimiento");
//Pasamos como parámetro al delegado el método CompareByBirthday
Sort(personas, Persona.CompareByBirthday);
foreach (Persona p in personas)
{
Console.WriteLine("Date: {0}, {1}, {2}",p.Birthday.Day, p.Birthday.Month,
p.Birthday.Year);
}

Console.WriteLine();
Console.WriteLine("Ordenando las personas por altura");
//Pasamos como parámetro al delegado el método CompareByHeight
Sort(personas, Persona.CompareByHeight);
foreach (Persona p in personas)
{
Console.WriteLine("Height: {0}",p.Height);
}
Console.ReadKey();
}
Que tal si ahora nos piden que ordenemos a las personas por orden alfabético
a partir de la segunda letra del nombre?
Solamente tendríamos que añadir el método:

//Comparamos por nombre en orden alfabético a partir d la segunda letra


public static int CompareFromSecondLetter(object x, object y)
{
Persona p1 = x as Persona;
Persona p2 = y as Persona;
//Usamos substring para comparar los nombres a partir de la segunda letra
return StringComparer.OrdinalIgnoreCase.Compare(p1.Name.Substring(1),
p2.Name.Substring(1));
}

y usarlo como:

Sort(personas, Persona.CompareFromSecondLetter);

Muchas gracias

www.asociacionaepi.es

Habilitar la depuración remota de


eventos en las app de SharePoint
Cuando estamos desarrollando apps para SharePoint, es posible depurar las app
de tipo autohosted o provider hosted aunque no se encuentren en el servidor
de SharePoint. Al utilizar un Service bus de Windows Azure, Visual Studio se
comunica con el mismo servicio de Windows Communication Foundation (WCF) que
los manejadores de evento remotos (remote event receivers y app event
receivers) usan. De esta forma, se evitan problemas de red entre la app
guardada en la nube y la app local y es posible depurar los eventos remotos.
Para crear el service bus, necesitamos una cuenta de Windows Azure. Una vez
hayamos entrado en la cuenta, en “Service bus”, le daremos a “crear”:
Habrá que escribir un nombre para el namespace del bus de datos y aceptar:

Hay que esperar un rato, mientras el Service bus se está activando:

Una vez que esté creada, hay que pinchar en ella y pinchar en “Información de
conexión”:
Copiamos la “Cadena de conexión”:

En Visual Studio, hacemos clic derecho en el proyecto de la app y vamos a


propiedades. En la pestaña de SharePoint, en la parte inferior, hay que
marcar la casilla “Enable remote event debugging” y pegar la cadena de
conexión:
Una vez hayamos hecho esto, podremos depurar eventos remotos en nuestra app.

www.asociacionaepi.es

Como configurar Android en Netbeans


Esta entrada está dedicada a aquellas personas que no les convence mucho la idea de
usar Eclipse para desarrollar en Android por el motivo que sea. Para configurar
Netbeans y poder crear nuestros proyectos de Android, tenemos que hacer lo siguiente:

Abrimos NetBeans
Vamos al menú Tools, Plugins, esperamos a que la ventana se cargue y entramos
en la pestaña Settings
Presionamos el botón Add.
En la siguiente ventana, en el campo Name escribimos el valor “NBAndroid”
Marcamos la casilla Check for updates automatically.
La ventana debe quedar así.

En el campo URL debemos añadir la


siguiente http://nbandroid.org/release72/updates/updates.xml. Luego
en Available Plugins se agregan NBAndroid Graddle Support, Android y
NBAndroid Extensions y se presiona Install.

Una vez hecho esto, ir a la pestaña Available Plugins, hay que localizar los
plugins Android y Android Test Runner for NetBeans 7.0+, marcarlos y
presionar el botón Install, presionamos Next, aceptamos los términos de la
licencia y finalmente apretamos el botón Install, esto descargará el plugin
en nuestro equipo, así que habrá que esperar a que termine la descarga.
Terminado este proceso, tendremos una advertencia mostrándonos que el plugin
no está oficialmente respaldado por Oracle, presionemos Continue. Después,
presionamos el botón Finish. Para activarlo, nos movemos a la
pestaña Installed, y podemos ver que efectivamente, ambos componentes están
instalados y activados. Regresando a NetBeans, presionemos F11 para compilar
el proyecto, luego F6 para probarlo. La primera vez, se nos solicitará que
seleccionemos el dispositivo a utilizar, deberíamos tener en ese lista el que
creamos con anterioridad bajo la sección Start AVD, seleccionemos nuestro
dispositivo y presionemos OK para ver nuestro proyecto (que no hace nada en
realidad) en el emulador seleccionado. Nos vemos en próximas entradas
Developers!.

www.asociacionaepi.es

Das könnte Ihnen auch gefallen