Ir al contenido

Capítulo 10: Clases y objetos (conceptos básicos)

Capítulo 10: Clases y objetos (conceptos básicos)

Sección titulada «Capítulo 10: Clases y objetos (conceptos básicos)»

Parte I - Sección 2: Programación desde cero - Nivel: Principiante


Al finalizar este capítulo serás capaz de:

  • Entender qué es una clase y qué es un objeto (plano vs instancia)
  • Crear clases propias con propiedades y métodos
  • Usar constructores para inicializar objetos
  • Instanciar objetos con la palabra clave new
  • Aplicar encapsulación básica con public/private
  • Comprender los conceptos de POO necesarios para trabajar con EPLAN API
  • Construir un sistema completo de gestión de componentes eléctricos usando POO

Antes de comenzar este capítulo debes:

  • ✅ Haber completado el Capítulo 9
  • ✅ Entender funciones y métodos
  • ✅ Saber trabajar con colecciones (List, Dictionary)
  • ✅ Conocer tipos de datos básicos (string, int, double, bool)

Sistema de Gestión de Componentes Eléctricos con POO

Al final de este capítulo habrás construido un sistema orientado a objetos completo para:

  • Modelar componentes eléctricos (Contactor, Guardamotor, Motor)
  • Crear circuitos con múltiples componentes
  • Calcular parámetros eléctricos (corriente total, potencia)
  • Validar compatibilidad de componentes
  • Generar reportes técnicos

Tiempo estimado: 70-80 minutos

⚠️ Nota Importante: En este capítulo, crearemos los objetos (contactores, motores, etc.) manualmente en el código instanciando las clases. Esto es intencional para comprender los fundamentos de la Programación Orientada a Objetos (POO). En capítulos posteriores, aprenderemos a crear estos objetos automáticamente a partir de los datos reales de EPLAN.


1. Introducción: ¿Por qué necesitamos POO?

Sección titulada «1. Introducción: ¿Por qué necesitamos POO?»

Hasta ahora has trabajado con variables individuales y colecciones. Pero, ¿cómo representamos un componente eléctrico completo con todos sus datos y comportamientos?

Enfoque sin POO (código difícil de mantener):

// Contactor K1
string k1_designacion = "K1";
string k1_codigo = "3RT2026-1BB40";
double k1_corriente = 25.0;
double k1_voltaje = 400.0;
string k1_fabricante = "Siemens";
// Contactor K2
string k2_designacion = "K2";
string k2_codigo = "3RT2036-1BB40";
double k2_corriente = 40.0;
double k2_voltaje = 400.0;
string k2_fabricante = "Siemens";
// ¿Y si necesitamos 100 contactores?
// ¿Cómo agrupamos los datos de cada componente?
// ¿Cómo calculamos la corriente total?

Enfoque con POO (código organizado y escalable):

// Crear clase Contactor
public class Contactor
{
public string Designacion { get; set; }
public string Codigo { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
public string Fabricante { get; set; }
public string ObtenerDescripcion()
{
return string.Format("{0} - {1} ({2}A, {3}V)",
Designacion, Codigo, Corriente, Voltaje);
}
}
// Crear objetos (instancias)
Contactor k1 = new Contactor();
k1.Designacion = "K1";
k1.Codigo = "3RT2026-1BB40";
k1.Corriente = 25.0;
k1.Voltaje = 400.0;
k1.Fabricante = "Siemens";
MessageBox.Show(k1.ObtenerDescripcion());
// "K1 - 3RT2026-1BB40 (25A, 400V)"

Ventajas de POO:

  • Organización: Datos y funciones relacionados juntos
  • Reutilización: Crea plantillas (clases) y úsalas múltiples veces
  • Mantenibilidad: Cambios centralizados
  • Escalabilidad: Fácil agregar nuevas funcionalidades
  • Claridad: El código se lee como el mundo real

Clase: Es el plano o plantilla que define cómo será algo.

Objeto: Es una instancia concreta creada a partir de la clase.

Analogía con planos arquitectónicos:

Clase Contactor → Plano de un contactor (especificación)
↓ instanciar
Objeto k1 → Contactor físico instalado en el tablero
Objeto k2 → Otro contactor físico del mismo tipo

Ejemplo visual:

// CLASE: Plano (definición)
public class Contactor
{
// Propiedades (datos)
public string Designacion { get; set; }
public double Corriente { get; set; }
// Métodos (comportamientos)
public void Activar()
{
MessageBox.Show(Designacion + " activado");
}
}
// OBJETOS: Instancias concretas
Contactor k1 = new Contactor(); // Primer contactor
Contactor k2 = new Contactor(); // Segundo contactor
Contactor k3 = new Contactor(); // Tercer contactor
// Cada objeto tiene sus propios valores
k1.Designacion = "K1";
k1.Corriente = 25.0;
k2.Designacion = "K2";
k2.Corriente = 40.0;
public class Contactor
{
// =====================================
// PROPIEDADES (Datos/Atributos)
// =====================================
public string Designacion { get; set; }
public string Codigo { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
// Propiedad privada (solo accesible dentro de la clase)
private bool estaActivado;
// =====================================
// CONSTRUCTOR (Inicialización)
// =====================================
public Contactor(string designacion, double corriente)
{
this.Designacion = designacion;
this.Corriente = corriente;
this.estaActivado = false;
}
// =====================================
// MÉTODOS (Comportamientos/Funciones)
// =====================================
public void Activar()
{
estaActivado = true;
MessageBox.Show(Designacion + " activado");
}
public double CalcularPotencia(double voltaje)
{
return Corriente * voltaje;
}
public string ObtenerEstado()
{
return estaActivado ? "Activado" : "Desactivado";
}
}

Las propiedades almacenan los datos de un objeto.

public class Componente
{
// Sintaxis simplificada
public string Designacion { get; set; }
public double Corriente { get; set; }
public string Fabricante { get; set; }
}
// Uso
Componente comp = new Componente();
comp.Designacion = "K1"; // set
string nombre = comp.Designacion; // get

Explicación:

  • { get; set; } crea automáticamente getters y setters
  • get: Permite leer el valor
  • set: Permite escribir el valor
public class Componente
{
// Solo se puede leer, no modificar desde fuera
public string Codigo { get; private set; }
public Componente(string codigo)
{
this.Codigo = codigo; // Solo se asigna en el constructor
}
}
// Uso
Componente comp = new Componente("3RT2026-1BB40");
MessageBox.Show(comp.Codigo); // ✓ OK - lectura permitida
comp.Codigo = "otro"; // ❌ ERROR - escritura no permitida
public class Motor
{
public double Potencia { get; set; } // kW
public double Voltaje { get; set; } // V
public double FactorPotencia { get; set; } // cosφ
// Propiedad calculada (sin almacenar valor)
public double CorrienteNominal
{
get
{
// I = P / (√3 × V × cosφ)
return (Potencia * 1000) / (1.732 * Voltaje * FactorPotencia);
}
}
}
// Uso
Motor m1 = new Motor();
m1.Potencia = 7.5; // kW
m1.Voltaje = 400.0; // V
m1.FactorPotencia = 0.85; // cosφ
double corriente = m1.CorrienteNominal; // Se calcula automáticamente
MessageBox.Show("Corriente nominal: " + corriente.ToString("F2") + "A");
// "Corriente nominal: 12.77A"
public class Guardamotor
{
public string Designacion { get; set; }
public double Corriente { get; set; }
// Valores por defecto
public string Tipo { get; set; } = "Guardamotor";
public int Polos { get; set; } = 3;
public bool ConAuxiliar { get; set; } = false;
}
// Uso
Guardamotor q1 = new Guardamotor();
MessageBox.Show(q1.Tipo); // "Guardamotor"
MessageBox.Show(q1.Polos.ToString()); // "3"

El constructor es un método especial que se ejecuta al crear un objeto con new.

4.1 Constructor sin parámetros (por defecto)

Sección titulada «4.1 Constructor sin parámetros (por defecto)»
public class Contactor
{
public string Designacion { get; set; }
public double Corriente { get; set; }
// Constructor vacío (opcional, se crea automáticamente si no lo defines)
public Contactor()
{
// Inicialización vacía
}
}
// Uso
Contactor k1 = new Contactor();
k1.Designacion = "K1";
k1.Corriente = 25.0;

4.2 Constructor con parámetros (recomendado)

Sección titulada «4.2 Constructor con parámetros (recomendado)»
public class Contactor
{
public string Designacion { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
// Constructor con parámetros
public Contactor(string designacion, double corriente, double voltaje)
{
this.Designacion = designacion;
this.Corriente = corriente;
this.Voltaje = voltaje;
}
}
// Uso (más limpio y seguro)
Contactor k1 = new Contactor("K1", 25.0, 400.0);
MessageBox.Show(k1.Designacion); // "K1"

La palabra clave this:

  • this.Designacion se refiere a la propiedad del objeto
  • designacion (sin this) se refiere al parámetro del constructor
public class Contactor
{
public string Designacion { get; set; }
public string Codigo { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
// Constructor 1: Solo designación y corriente
public Contactor(string designacion, double corriente)
{
this.Designacion = designacion;
this.Corriente = corriente;
this.Voltaje = 400.0; // Valor por defecto
}
// Constructor 2: Todos los parámetros
public Contactor(string designacion, string codigo, double corriente, double voltaje)
{
this.Designacion = designacion;
this.Codigo = codigo;
this.Corriente = corriente;
this.Voltaje = voltaje;
}
}
// Uso de diferentes constructores
Contactor k1 = new Contactor("K1", 25.0);
Contactor k2 = new Contactor("K2", "3RT2036-1BB40", 40.0, 400.0);
public class Contactor
{
public string Designacion { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
// Constructor principal
public Contactor(string designacion, double corriente, double voltaje)
{
this.Designacion = designacion;
this.Corriente = corriente;
this.Voltaje = voltaje;
}
// Constructor simplificado que llama al principal
public Contactor(string designacion, double corriente)
: this(designacion, corriente, 400.0) // Llama al constructor principal
{
// Voltaje = 400V por defecto
}
}

Los métodos son las funciones que definen el comportamiento de un objeto.

public class Contactor
{
public string Designacion { get; set; }
private bool estaActivado;
public void Activar()
{
estaActivado = true;
MessageBox.Show(Designacion + " activado");
}
public void Desactivar()
{
estaActivado = false;
MessageBox.Show(Designacion + " desactivado");
}
}
// Uso
Contactor k1 = new Contactor();
k1.Designacion = "K1";
k1.Activar(); // Muestra mensaje
k1.Desactivar(); // Muestra mensaje
public class Motor
{
public double Potencia { get; set; } // kW
public double Voltaje { get; set; } // V
// Método que retorna un valor
public double CalcularCorriente(double factorPotencia)
{
// I = P / (√3 × V × cosφ)
double corriente = (Potencia * 1000) / (1.732 * Voltaje * factorPotencia);
return corriente;
}
public string ObtenerClasificacion()
{
if (Potencia < 1.0)
return "Motor pequeño";
else if (Potencia < 10.0)
return "Motor mediano";
else
return "Motor grande";
}
}
// Uso
Motor m1 = new Motor();
m1.Potencia = 7.5;
m1.Voltaje = 400.0;
double corriente = m1.CalcularCorriente(0.85);
MessageBox.Show("Corriente: " + corriente.ToString("F2") + "A");
string clasificacion = m1.ObtenerClasificacion();
MessageBox.Show("Clasificación: " + clasificacion); // "Motor mediano"
public class Componente
{
public string Designacion { get; set; }
public double Corriente { get; set; }
// Parámetros opcionales con valores por defecto
public string GenerarEtiqueta(bool incluirCorriente = true, string prefijo = "")
{
string etiqueta = prefijo + Designacion;
if (incluirCorriente)
{
etiqueta += " (" + Corriente + "A)";
}
return etiqueta;
}
}
// Uso
Componente c = new Componente();
c.Designacion = "K1";
c.Corriente = 25.0;
MessageBox.Show(c.GenerarEtiqueta()); // "K1 (25A)"
MessageBox.Show(c.GenerarEtiqueta(false)); // "K1"
MessageBox.Show(c.GenerarEtiqueta(true, "CONT-")); // "CONT-K1 (25A)"
MessageBox.Show(c.GenerarEtiqueta(prefijo: "CONT-")); // "CONT-K1 (25A)"

La encapsulación es el principio de ocultar detalles internos y exponer solo lo necesario.

public class Contactor
{
// PUBLIC: Accesible desde fuera de la clase
public string Designacion { get; set; }
public double Corriente { get; set; }
// PRIVATE: Solo accesible dentro de la clase
private bool estaActivado;
private int contadorActivaciones;
public void Activar()
{
estaActivado = true; // ✓ OK - acceso interno
contadorActivaciones++; // ✓ OK - acceso interno
MessageBox.Show(Designacion + " activado");
}
public int ObtenerContadorActivaciones()
{
return contadorActivaciones; // ✓ OK - retornar valor privado
}
}
// Uso
Contactor k1 = new Contactor();
k1.Designacion = "K1"; // ✓ OK - public
k1.Activar(); // ✓ OK - public
int contador = k1.ObtenerContadorActivaciones(); // ✓ OK - método public
MessageBox.Show("Activaciones: " + contador);
// k1.estaActivado = true; // ❌ ERROR - private no accesible
// k1.contadorActivaciones++; // ❌ ERROR - private no accesible

Ventajas de la encapsulación:

public class Motor
{
public double Potencia { get; set; } // kW
// Corriente es privada y se calcula automáticamente
private double corrienteNominal;
// Método público para establecer potencia y calcular corriente
public void EstablecerPotencia(double potencia, double voltaje, double fp)
{
this.Potencia = potencia;
// Cálculo interno (encapsulado)
this.corrienteNominal = (potencia * 1000) / (1.732 * voltaje * fp);
}
// Método público para obtener corriente
public double ObtenerCorriente()
{
return corrienteNominal;
}
}

Beneficios:

  • ✅ Protege datos sensibles
  • ✅ Evita modificaciones incorrectas
  • ✅ Centraliza lógica de validación
  • ✅ Permite cambiar implementación interna sin afectar código externo

this se refiere al objeto actual.

7.1 Distinguir entre parámetros y propiedades

Sección titulada «7.1 Distinguir entre parámetros y propiedades»
public class Contactor
{
public string Designacion { get; set; }
public double Corriente { get; set; }
public Contactor(string Designacion, double Corriente)
{
// Sin this: ambigüedad
// Designacion = Designacion; // ¿Cuál es cuál?
// Con this: claridad
this.Designacion = Designacion; // this.Designacion = propiedad
this.Corriente = Corriente; // Corriente = parámetro
}
}
public class Circuito
{
private List<Contactor> contactores;
public void AgregarContactor(Contactor contactor)
{
contactores.Add(contactor);
}
}
public class Contactor
{
public string Designacion { get; set; }
public void RegistrarEnCircuito(Circuito circuito)
{
circuito.AgregarContactor(this); // Pasar el objeto actual
}
}
// Uso
Circuito circuito1 = new Circuito();
Contactor k1 = new Contactor();
k1.Designacion = "K1";
k1.RegistrarEnCircuito(circuito1); // k1 se agrega a sí mismo al circuito

💻 Manos a la obra: Construyendo el proyecto

Sección titulada «💻 Manos a la obra: Construyendo el proyecto»

Vamos a crear un Sistema de Gestión de Componentes Eléctricos completo usando POO.

public class ComponenteElectrico
{
// ========================================
// PROPIEDADES
// ========================================
public string Designacion { get; set; }
public string Codigo { get; set; }
public string Tipo { get; set; }
public double Corriente { get; set; } // A
public double Voltaje { get; set; } // V
public string Fabricante { get; set; }
// Propiedad calculada
public double Potencia
{
get
{
return Corriente * Voltaje; // P = I × V (aproximado)
}
}
// ========================================
// CONSTRUCTOR
// ========================================
public ComponenteElectrico(string designacion, string codigo, string tipo,
double corriente, double voltaje, string fabricante)
{
this.Designacion = designacion;
this.Codigo = codigo;
this.Tipo = tipo;
this.Corriente = corriente;
this.Voltaje = voltaje;
this.Fabricante = fabricante;
}
// Constructor simplificado
public ComponenteElectrico(string designacion, double corriente)
{
this.Designacion = designacion;
this.Corriente = corriente;
this.Voltaje = 400.0; // Por defecto
this.Tipo = "Genérico";
}
// ========================================
// MÉTODOS
// ========================================
public string ObtenerDescripcionCompleta()
{
return string.Format("{0} [{1}]\n Código: {2}\n Corriente: {3}A\n Voltaje: {4}V\n Potencia: {5:F2}W\n Fabricante: {6}",
Designacion, Tipo, Codigo ?? "N/A", Corriente, Voltaje, Potencia, Fabricante ?? "N/A");
}
public string ObtenerDescripcionCorta()
{
return string.Format("{0} - {1} ({2}A, {3}V)",
Designacion, Tipo, Corriente, Voltaje);
}
public bool EsCompatibleConVoltaje(double voltajeRequerido)
{
// Tolerancia del 10%
double tolerancia = voltajeRequerido * 0.10;
return Math.Abs(Voltaje - voltajeRequerido) <= tolerancia;
}
public void MostrarInformacion()
{
MessageBox.Show(ObtenerDescripcionCompleta(), "Información del Componente");
}
}

¿Qué acabamos de hacer?

Creamos una clase base ComponenteElectrico que:

  • Almacena datos eléctricos básicos (corriente, voltaje, código)
  • Calcula potencia automáticamente
  • Tiene dos constructores (completo y simplificado)
  • Provee métodos para obtener descripciones formateadas
  • Valida compatibilidad de voltaje

public class Contactor : ComponenteElectrico
{
// ========================================
// PROPIEDADES ESPECÍFICAS
// ========================================
public int Polos { get; set; }
public string CategoriaUso { get; set; } // AC1, AC3, AC4
private bool estaActivado;
private int contadorActivaciones;
// ========================================
// CONSTRUCTOR
// ========================================
public Contactor(string designacion, string codigo, double corrienteAC3,
double voltaje, int polos, string fabricante)
: base(designacion, codigo, "Contactor", corrienteAC3, voltaje, fabricante)
{
this.Polos = polos;
this.CategoriaUso = "AC3";
this.estaActivado = false;
this.contadorActivaciones = 0;
}
// ========================================
// MÉTODOS ESPECÍFICOS
// ========================================
public void Activar()
{
if (!estaActivado)
{
estaActivado = true;
contadorActivaciones++;
MessageBox.Show(string.Format("Contactor {0} ACTIVADO\nActivaciones totales: {1}",
Designacion, contadorActivaciones));
}
else
{
MessageBox.Show(Designacion + " ya está activado");
}
}
public void Desactivar()
{
if (estaActivado)
{
estaActivado = false;
MessageBox.Show(Designacion + " desactivado");
}
}
public string ObtenerEstado()
{
return estaActivado ? "ACTIVADO" : "DESACTIVADO";
}
public int ObtenerContadorActivaciones()
{
return contadorActivaciones;
}
}

public class Circuito
{
// ========================================
// PROPIEDADES
// ========================================
public string Nombre { get; set; }
public double VoltajeNominal { get; set; }
private List<ComponenteElectrico> componentes;
// ========================================
// CONSTRUCTOR
// ========================================
public Circuito(string nombre, double voltajeNominal)
{
this.Nombre = nombre;
this.VoltajeNominal = voltajeNominal;
this.componentes = new List<ComponenteElectrico>();
}
// ========================================
// MÉTODOS DE GESTIÓN
// ========================================
public bool AgregarComponente(ComponenteElectrico componente)
{
// Validar compatibilidad de voltaje
if (!componente.EsCompatibleConVoltaje(VoltajeNominal))
{
MessageBox.Show(string.Format("Error: {0} no es compatible con {1}V\n(Voltaje del componente: {2}V)",
componente.Designacion, VoltajeNominal, componente.Voltaje),
"Voltaje incompatible");
return false;
}
componentes.Add(componente);
MessageBox.Show(string.Format("{0} agregado al circuito {1}",
componente.Designacion, Nombre));
return true;
}
public void EliminarComponente(string designacion)
{
ComponenteElectrico comp = componentes.Find(c => c.Designacion == designacion);
if (comp != null)
{
componentes.Remove(comp);
MessageBox.Show(designacion + " eliminado del circuito");
}
else
{
MessageBox.Show(designacion + " no encontrado");
}
}
public ComponenteElectrico BuscarComponente(string designacion)
{
return componentes.Find(c => c.Designacion == designacion);
}
// ========================================
// MÉTODOS DE CÁLCULO
// ========================================
public double CalcularCorrienteTotal()
{
double total = 0;
foreach (ComponenteElectrico comp in componentes)
{
total += comp.Corriente;
}
return total;
}
public double CalcularPotenciaTotal()
{
double total = 0;
foreach (ComponenteElectrico comp in componentes)
{
total += comp.Potencia;
}
return total;
}
public int ObtenerCantidadComponentes()
{
return componentes.Count;
}
// ========================================
// MÉTODOS DE REPORTE
// ========================================
public void MostrarResumen()
{
string reporte = "=======================================\n";
reporte += string.Format("CIRCUITO: {0}\n", Nombre);
reporte += string.Format("Voltaje nominal: {0}V\n", VoltajeNominal);
reporte += "=======================================\n\n";
reporte += string.Format("Componentes: {0}\n", componentes.Count);
reporte += string.Format("Corriente total: {0:F2}A\n", CalcularCorrienteTotal());
reporte += string.Format("Potencia total: {0:F2}W\n\n", CalcularPotenciaTotal());
reporte += "LISTA DE COMPONENTES:\n";
reporte += "---------------------------------------\n";
foreach (ComponenteElectrico comp in componentes)
{
reporte += comp.ObtenerDescripcionCorta() + "\n";
}
MessageBox.Show(reporte, "Resumen del Circuito");
}
public void MostrarDetalleCompleto()
{
string reporte = string.Format("CIRCUITO: {0} ({1}V)\n\n", Nombre, VoltajeNominal);
if (componentes.Count == 0)
{
reporte += "No hay componentes en este circuito.";
}
else
{
foreach (ComponenteElectrico comp in componentes)
{
reporte += comp.ObtenerDescripcionCompleta();
reporte += "\n\n---------------------------------------\n\n";
}
}
MessageBox.Show(reporte, "Detalle Completo del Circuito");
}
}

//
// Proyecto: Sistema de Gestión de Componentes Eléctricos con POO
// Capítulo: 10
// Descripción: Sistema completo para gestionar componentes eléctricos usando
// programación orientada a objetos (clases, propiedades, métodos,
// constructores, encapsulación)
// Autor: C.D. López
// Fecha: Enero 2025
//
using System;
using System.Collections.Generic;
using Eplan.EplApi.Base;
using Eplan.EplApi.ApplicationFramework;
// ========================================
// CLASE BASE: ComponenteElectrico
// ========================================
public class ComponenteElectrico
{
// Propiedades
public string Designacion { get; set; }
public string Codigo { get; set; }
public string Tipo { get; set; }
public double Corriente { get; set; }
public double Voltaje { get; set; }
public string Fabricante { get; set; }
// Propiedad calculada
public double Potencia
{
get { return Corriente * Voltaje; }
}
// Constructores
public ComponenteElectrico(string designacion, string codigo, string tipo,
double corriente, double voltaje, string fabricante)
{
this.Designacion = designacion;
this.Codigo = codigo;
this.Tipo = tipo;
this.Corriente = corriente;
this.Voltaje = voltaje;
this.Fabricante = fabricante;
}
public ComponenteElectrico(string designacion, double corriente)
{
this.Designacion = designacion;
this.Corriente = corriente;
this.Voltaje = 400.0;
this.Tipo = "Genérico";
}
// Métodos
public string ObtenerDescripcionCompleta()
{
return string.Format("{0} [{1}]\n Código: {2}\n Corriente: {3}A\n Voltaje: {4}V\n Potencia: {5:F2}W\n Fabricante: {6}",
Designacion, Tipo, Codigo ?? "N/A", Corriente, Voltaje, Potencia, Fabricante ?? "N/A");
}
public string ObtenerDescripcionCorta()
{
return string.Format("{0} - {1} ({2}A, {3}V)",
Designacion, Tipo, Corriente, Voltaje);
}
public bool EsCompatibleConVoltaje(double voltajeRequerido)
{
double tolerancia = voltajeRequerido * 0.10;
return Math.Abs(Voltaje - voltajeRequerido) <= tolerancia;
}
public void MostrarInformacion()
{
MessageBox.Show(ObtenerDescripcionCompleta(), "Información del Componente");
}
}
// ========================================
// CLASE ESPECIALIZADA: Contactor
// ========================================
public class Contactor : ComponenteElectrico
{
public int Polos { get; set; }
public string CategoriaUso { get; set; }
private bool estaActivado;
private int contadorActivaciones;
public Contactor(string designacion, string codigo, double corrienteAC3,
double voltaje, int polos, string fabricante)
: base(designacion, codigo, "Contactor", corrienteAC3, voltaje, fabricante)
{
this.Polos = polos;
this.CategoriaUso = "AC3";
this.estaActivado = false;
this.contadorActivaciones = 0;
}
public void Activar()
{
if (!estaActivado)
{
estaActivado = true;
contadorActivaciones++;
MessageBox.Show(string.Format("Contactor {0} ACTIVADO\nActivaciones totales: {1}",
Designacion, contadorActivaciones));
}
else
{
MessageBox.Show(Designacion + " ya está activado");
}
}
public void Desactivar()
{
if (estaActivado)
{
estaActivado = false;
MessageBox.Show(Designacion + " desactivado");
}
}
public string ObtenerEstado()
{
return estaActivado ? "ACTIVADO" : "DESACTIVADO";
}
public int ObtenerContadorActivaciones()
{
return contadorActivaciones;
}
}
// ========================================
// CLASE CONTENEDOR: Circuito
// ========================================
public class Circuito
{
public string Nombre { get; set; }
public double VoltajeNominal { get; set; }
private List<ComponenteElectrico> componentes;
public Circuito(string nombre, double voltajeNominal)
{
this.Nombre = nombre;
this.VoltajeNominal = voltajeNominal;
this.componentes = new List<ComponenteElectrico>();
}
public bool AgregarComponente(ComponenteElectrico componente)
{
if (!componente.EsCompatibleConVoltaje(VoltajeNominal))
{
MessageBox.Show(string.Format("Error: {0} no es compatible con {1}V\n(Voltaje del componente: {2}V)",
componente.Designacion, VoltajeNominal, componente.Voltaje),
"Voltaje incompatible");
return false;
}
componentes.Add(componente);
MessageBox.Show(string.Format("{0} agregado al circuito {1}",
componente.Designacion, Nombre));
return true;
}
public void EliminarComponente(string designacion)
{
ComponenteElectrico comp = componentes.Find(c => c.Designacion == designacion);
if (comp != null)
{
componentes.Remove(comp);
MessageBox.Show(designacion + " eliminado del circuito");
}
else
{
MessageBox.Show(designacion + " no encontrado");
}
}
public ComponenteElectrico BuscarComponente(string designacion)
{
return componentes.Find(c => c.Designacion == designacion);
}
public double CalcularCorrienteTotal()
{
double total = 0;
foreach (ComponenteElectrico comp in componentes)
{
total += comp.Corriente;
}
return total;
}
public double CalcularPotenciaTotal()
{
double total = 0;
foreach (ComponenteElectrico comp in componentes)
{
total += comp.Potencia;
}
return total;
}
public int ObtenerCantidadComponentes()
{
return componentes.Count;
}
public void MostrarResumen()
{
string reporte = "=======================================\n";
reporte += string.Format("CIRCUITO: {0}\n", Nombre);
reporte += string.Format("Voltaje nominal: {0}V\n", VoltajeNominal);
reporte += "=======================================\n\n";
reporte += string.Format("Componentes: {0}\n", componentes.Count);
reporte += string.Format("Corriente total: {0:F2}A\n", CalcularCorrienteTotal());
reporte += string.Format("Potencia total: {0:F2}W\n\n", CalcularPotenciaTotal());
reporte += "LISTA DE COMPONENTES:\n";
reporte += "---------------------------------------\n";
foreach (ComponenteElectrico comp in componentes)
{
reporte += comp.ObtenerDescripcionCorta() + "\n";
}
MessageBox.Show(reporte, "Resumen del Circuito");
}
public void MostrarDetalleCompleto()
{
string reporte = string.Format("CIRCUITO: {0} ({1}V)\n\n", Nombre, VoltajeNominal);
if (componentes.Count == 0)
{
reporte += "No hay componentes en este circuito.";
}
else
{
foreach (ComponenteElectrico comp in componentes)
{
reporte += comp.ObtenerDescripcionCompleta();
reporte += "\n\n---------------------------------------\n\n";
}
}
MessageBox.Show(reporte, "Detalle Completo del Circuito");
}
}
// ========================================
// SCRIPT PRINCIPAL DE DEMOSTRACIÓN
// ========================================
public class GestionComponentesPOO
{
[Start]
public void Ejecutar()
{
try
{
// Crear circuito
Circuito circuitoPrincipal = new Circuito("Circuito Motor Principal", 400.0);
// Crear componentes
Contactor k1 = new Contactor("K1", "3RT2026-1BB40", 25.0, 400.0, 3, "Siemens");
Contactor k2 = new Contactor("K2", "3RT2036-1BB40", 40.0, 400.0, 3, "Siemens");
ComponenteElectrico q1 = new ComponenteElectrico("Q1", "3RV2011-1JA10", "Guardamotor",
4.5, 400.0, "Siemens");
ComponenteElectrico m1 = new ComponenteElectrico("M1", 15.0); // Constructor simple
m1.Tipo = "Motor";
m1.Fabricante = "WEG";
// Agregar componentes al circuito
circuitoPrincipal.AgregarComponente(k1);
circuitoPrincipal.AgregarComponente(k2);
circuitoPrincipal.AgregarComponente(q1);
circuitoPrincipal.AgregarComponente(m1);
// Mostrar resumen
circuitoPrincipal.MostrarResumen();
// Activar contactores
k1.Activar();
k1.Activar(); // Intento de activar de nuevo
k2.Activar();
// Buscar y mostrar componente específico
ComponenteElectrico componenteBuscado = circuitoPrincipal.BuscarComponente("Q1");
if (componenteBuscado != null)
{
componenteBuscado.MostrarInformacion();
}
// Mostrar detalle completo
circuitoPrincipal.MostrarDetalleCompleto();
// Intentar agregar componente incompatible (220V en circuito 400V)
ComponenteElectrico x1 = new ComponenteElectrico("X1", "XYZ-123", "Relé",
2.0, 220.0, "Generic");
circuitoPrincipal.AgregarComponente(x1); // Debería fallar
// Mostrar contadores de activaciones
MessageBox.Show(string.Format("K1 activado {0} veces\nK2 activado {1} veces",
k1.ObtenerContadorActivaciones(),
k2.ObtenerContadorActivaciones()));
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message, "Error en la ejecución");
}
}
}

En el código usamos herencia cuando Contactor : ComponenteElectrico.

public class Contactor : ComponenteElectrico
{
// Hereda todas las propiedades y métodos de ComponenteElectrico
// Puede agregar propiedades y métodos específicos
}

Beneficios:

  • ✅ Reutilización de código
  • ✅ Jerarquía lógica (especialización)
  • ✅ Polimorfismo (veremos en capítulos avanzados)

Campo (field):

private bool estaActivado; // Almacenamiento directo

Propiedad (property):

public string Designacion { get; set; } // Con getters/setters automáticos

Cuándo usar cada uno:

  • Propiedades públicas: Para datos que se acceden desde fuera
  • Campos privados: Para datos internos que no se exponen
Codigo ?? "N/A"

Significa: “Si Codigo es null, usa "N/A" en su lugar”.

  • Hacer: Nombrar clases con sustantivos (Contactor, Motor)
  • Hacer: Nombrar métodos con verbos (Activar(), Calcular())
  • Hacer: Usar constructores para inicializar objetos correctamente
  • Hacer: Encapsular (private) datos internos
  • Hacer: Validar datos en constructores y métodos públicos
  • Evitar: Exponer todos los campos como public
  • Evitar: Constructores sin validación
  • Evitar: Métodos que hacen demasiadas cosas (responsabilidad única)

Síntoma: Error “Use of unassigned variable”

Causa: No instanciar el objeto

// ❌ MAL
Contactor k1;
k1.Designacion = "K1"; // ERROR: k1 no está inicializado
// ✓ BIEN
Contactor k1 = new Contactor("K1", "3RT2026", 25.0, 400.0, 3, "Siemens");
k1.Designacion = "K1"; // OK

Síntoma: Error “An object reference is required…”

Causa: Intentar llamar métodos de instancia en la clase

// ❌ MAL
Contactor.Activar(); // ERROR: Contactor es la clase, no un objeto
// ✓ BIEN
Contactor k1 = new Contactor(...);
k1.Activar(); // OK: k1 es un objeto

Síntoma: Error “Object reference not set to an instance of an object”

Causa: Usar un objeto que es null

Circuito circuito = null;
circuito.AgregarComponente(k1); // ❌ ERROR: circuito es null
// Solución: Siempre inicializar
Circuito circuito = new Circuito("Principal", 400.0);
circuito.AgregarComponente(k1); // ✓ OK

Desafío 1: Clase Motor con cálculo de corriente

Sección titulada «Desafío 1: Clase Motor con cálculo de corriente»

Dificultad: ⭐☆☆

Crea una clase Motor que herede de ComponenteElectrico y que calcule automáticamente la corriente nominal basada en potencia, voltaje y factor de potencia.

💡 Pista

Usa la fórmula: I = P / (√3 × V × cosφ)

Crea una propiedad FactorPotencia y un método CalcularCorriente().

✅ Solución
public class Motor : ComponenteElectrico
{
public double PotenciaKW { get; set; }
public double FactorPotencia { get; set; }
public int Polos { get; set; }
public double Rpm { get; set; }
public Motor(string designacion, double potenciaKW, double voltaje,
double factorPotencia, int polos, string fabricante)
: base(designacion, "", "Motor", 0, voltaje, fabricante)
{
this.PotenciaKW = potenciaKW;
this.FactorPotencia = factorPotencia;
this.Polos = polos;
// Calcular corriente automáticamente
this.Corriente = CalcularCorrienteNominal();
// Calcular RPM aproximadas (frecuencia 50Hz)
this.Rpm = (120 * 50) / polos;
}
private double CalcularCorrienteNominal()
{
// I = P / (√3 × V × cosφ)
return (PotenciaKW * 1000) / (1.732 * Voltaje * FactorPotencia);
}
public string ObtenerDatosMotor()
{
return string.Format("{0}\nPotencia: {1}kW\nRPM: {2}\nFactor de potencia: {3}\nCorriente nominal: {4:F2}A",
Designacion, PotenciaKW, Rpm, FactorPotencia, Corriente);
}
}
// Uso
Motor m1 = new Motor("M1", 7.5, 400.0, 0.85, 4, "WEG");
MessageBox.Show(m1.ObtenerDatosMotor());

Desafío 2: Clase Guardamotor con rango de regulación

Sección titulada «Desafío 2: Clase Guardamotor con rango de regulación»

Dificultad: ⭐⭐☆

Crea una clase Guardamotor con propiedades CorrienteMin y CorrienteMax (rango de regulación). Agrega un método que valide si una corriente dada está dentro del rango.

💡 Pista

Usa propiedades CorrienteMin y CorrienteMax, y crea método EsValidaParaMotor(double corrienteMotor).


Dificultad: ⭐⭐⭐

Agrega un método a la clase Circuito que exporte toda la información del circuito a un archivo de texto (.txt) con formato profesional.

💡 Pista

Usa System.IO.File.WriteAllText() y construye un string con formato tabular.


En este capítulo aprendiste:

  • Qué es POO: Programación orientada a objetos como forma de organizar código
  • Clase vs Objeto: Plano (clase) vs instancia concreta (objeto)
  • Propiedades: Almacenar datos del objeto (public/private, calculadas)
  • Constructores: Inicializar objetos correctamente con new
  • Métodos: Definir comportamientos del objeto
  • Encapsulación: Ocultar detalles internos (public/private)
  • Herencia básica: Reutilizar código con class Hijo : Padre
  • Proyecto construido: Sistema completo de gestión de componentes eléctricos

Conceptos clave:

  • Las clases son plantillas, los objetos son instancias
  • new crea un nuevo objeto a partir de la clase
  • Las propiedades almacenan datos, los métodos definen comportamientos
  • La encapsulación protege datos internos (private) y expone interfaz pública (public)
  • POO te prepara para trabajar con EPLAN API (que usa POO intensivamente)

Capítulo 9: Colecciones de datos

Capítulo 11: Manejo de errores

En el próximo capítulo aprenderás a manejar errores y excepciones de forma robusta usando try-catch-finally. Construirás validadores que no fallan ante datos incorrectos y aprenderás logging profesional.



P: ¿Cuál es la diferencia entre una clase y un objeto?

Sección titulada «P: ¿Cuál es la diferencia entre una clase y un objeto?»

R: Una clase es el plano o plantilla (ej. class Contactor). Un objeto es una instancia concreta creada con new (ej. Contactor k1 = new Contactor(...)). Puedes crear muchos objetos a partir de una sola clase.

P: ¿Cuándo debo usar public y cuándo private?

Sección titulada «P: ¿Cuándo debo usar public y cuándo private?»

R: Usa public para propiedades y métodos que necesitan ser accesibles desde fuera de la clase. Usa private para datos internos que no deben ser modificados directamente. Regla general: expón solo lo necesario.

P: ¿Puedo tener múltiples constructores en una clase?

Sección titulada «P: ¿Puedo tener múltiples constructores en una clase?»

R: Sí, se llama sobrecarga de constructores. Cada constructor debe tener diferente número o tipo de parámetros.

R: this se refiere al objeto actual. Úsalo para:

  1. Distinguir entre parámetros y propiedades con el mismo nombre
  2. Llamar a otro constructor desde un constructor
  3. Pasar el objeto actual a otro método

P: ¿Necesito entender POO avanzado para trabajar con EPLAN API?

Sección titulada «P: ¿Necesito entender POO avanzado para trabajar con EPLAN API?»

R: No. Con lo aprendido en este capítulo (clases, objetos, propiedades, métodos, constructores) es suficiente para usar EPLAN API. Los conceptos avanzados (interfaces, polimorfismo, clases abstractas) son opcionales.

R: La herencia permite crear una clase nueva (hijo) basada en una clase existente (padre). La clase hijo hereda todas las propiedades y métodos del padre, y puede agregar los suyos propios o sobrescribirlos.

public class ComponenteElectrico { ... } // Padre
public class Contactor : ComponenteElectrico { ... } // Hijo hereda de padre

Antes de pasar al siguiente capítulo, asegúrate de:

  • Entender la diferencia entre clase y objeto
  • Poder crear clases con propiedades y métodos
  • Saber usar constructores para inicializar objetos
  • Entender public vs private (encapsulación)
  • Poder instanciar objetos con new
  • Comprender qué es la herencia básica
  • Haber ejecutado el sistema de gestión de componentes
  • Poder agregar nuevas clases (Motor, Guardamotor)
  • (Opcional) Haber intentado al menos un desafío

¿Listo para aprender manejo robusto de errores? ¡Vamos al Capítulo 11! 🚀


Última actualización: Enero 2025 Tiempo de lectura estimado: 70-80 minutos Código de ejemplo: code/cap-10/