Modificadores de acceso a fondo
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 39 de 91.
Ahora que entiendes los tres modificadores de acceso, exploremos cómo se comportan en escenarios de herencia. La idea clave es que los miembros protected se vuelven accesibles para las clases hijas, mientras que los miembros private permanecen ocultos incluso para las hijas.
<?php
class Vehicle {
public $brand;
protected $engineType;
private $serialNumber;
public function __construct($brand, $engine, $serial) {
$this->brand = $brand;
$this->engineType = $engine;
$this->serialNumber = $serial;
}
private function getSerial() {
return $this->serialNumber;
}
protected function getEngineInfo() {
return $this->engineType;
}
}
class Car extends Vehicle {
public function getDetails() {
return $this->brand . " - " . $this->getEngineInfo();
// $this->serialNumber causaría un error - ¡privado!
// $this->getSerial() también fallaría - ¡método privado!
}
}
$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails();
Resultado:
Toyota - V6La clase Car puede acceder a $brand (público) y llamar a getEngineInfo() (protegido), pero no tiene acceso a $serialNumber o getSerial() porque son privados para Vehicle. Esta distinción te permite compartir algunos detalles internos con las clases hijas mientras mantienes otros completamente ocultos.
Punto clave: Usa protected cuando las clases hijas necesiten acceso a datos o métodos internos, y private cuando incluso las clases hijas no deban tocar ciertos detalles de implementación.
Desafío
FácilVamos a construir un sistema de gestión de dispositivos que explore cómo se comportan los modificadores de acceso a través de la herencia. Crearás una clase base Device con propiedades en diferentes niveles de visibilidad, y luego la extenderás con una clase Smartphone que demuestre a qué miembros pueden y no pueden acceder las clases hijas.
Organizarás tu código en tres archivos:
Device.php— Crea una claseDeviceque represente dispositivos electrónicos con diferentes niveles de sensibilidad de datos. Incluye:- Una propiedad
public$brandpara el nombre del fabricante - Una propiedad
protected$modelNumberpara identificación interna - Una propiedad
private$manufacturingCodepara datos exclusivos de fábrica
privategetManufacturingCode()que devuelva el código de fabricación. Añade un métodopublicgetModelInfo()que devuelva"Model: [modelNumber]". Finalmente, añade un métodopublicgetFullCode()que devuelva"[brand]-[manufacturingCode]"— esto demuestra cómo la propia clase puede acceder a sus propios miembros privados.- Una propiedad
Smartphone.php— Crea una claseSmartphoneque extienda deDevice. Incluye el archivo Device. El constructor debe aceptar brand, modelNumber, manufacturingCode y un nombre de sistema operativo. Llama al constructor padre con los tres primeros valores, luego almacena el OS en una propiedad privada. Añade un método públicogetDeviceDetails()que devuelva"[brand] [modelNumber] running [os]". Observa cómo puedes acceder directamente al$brandpúblico y al$modelNumberprotegido, pero no puedes acceder directamente al$manufacturingCodeprivado — en su lugar, tendrías que usar el método público heredadogetFullCode().main.php— Incluye el archivo Smartphone. Recibirás cuatro entradas: un nombre de marca, un número de modelo, un código de fabricación y un sistema operativo. Crea una instancia deSmartphonecon estos valores. Imprime tres líneas:- El resultado de llamar a
getDeviceDetails() - El resultado de llamar al método heredado
getModelInfo() - El resultado de llamar al método heredado
getFullCode()
- El resultado de llamar a
Este desafío resalta la distinción clave: tu clase Smartphone puede usar libremente el $modelNumber protegido heredado de Device, pero el $manufacturingCode privado y getManufacturingCode() permanecen completamente ocultos — accesibles solo a través de la interfaz pública que Device decide exponer.
Hoja de referencia
En la herencia, los miembros protected son accesibles para las clases hijas, mientras que los miembros private permanecen ocultos incluso para las hijas.
<?php
class Vehicle {
public $brand;
protected $engineType;
private $serialNumber;
public function __construct($brand, $engine, $serial) {
$this->brand = $brand;
$this->engineType = $engine;
$this->serialNumber = $serial;
}
private function getSerial() {
return $this->serialNumber;
}
protected function getEngineInfo() {
return $this->engineType;
}
}
class Car extends Vehicle {
public function getDetails() {
return $this->brand . " - " . $this->getEngineInfo();
// $this->serialNumber causaría un error - ¡privado!
// $this->getSerial() también fallaría - ¡método privado!
}
}
$car = new Car("Toyota", "V6", "ABC123");
echo $car->getDetails(); // Toyota - V6
Las clases hijas pueden acceder a:
- propiedades y métodos
public - propiedades y métodos
protected
Las clases hijas no pueden acceder a:
- propiedades y métodos
private(estos permanecen exclusivos de la clase padre)
Mejor práctica: Use protected cuando las clases hijas necesiten acceso a datos o métodos internos, y private cuando incluso las clases hijas no deban tocar ciertos detalles de implementación.
Pruébalo tú mismo
<?php
require_once 'Smartphone.php';
// Leer entradas
$brand = trim(fgets(STDIN));
$modelNumber = trim(fgets(STDIN));
$manufacturingCode = trim(fgets(STDIN));
$os = trim(fgets(STDIN));
// TODO: Crear una instancia de Smartphone con los valores de entrada
// TODO: Imprimir el resultado de getDeviceDetails()
// TODO: Imprimir el resultado del método heredado getModelInfo()
// TODO: Imprimir el resultado del método heredado getFullCode()
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy