¿Deben encapsularse todos los complementos en una clase?

28

Al desarrollar un complemento, ¿deberían agruparse las funciones en una Clase para evitar conflictos de espacio de nombres?

¿El uso de clases crea gastos generales de rendimiento para PHP?

Si hay un impacto en el rendimiento, ¿deberían los nombres de las funciones ser simplemente arreglados en su lugar?

Jamie
fuente
8
Probablemente más de una pregunta de PHP que una de WordPress, vea si esta pregunta de Stackoverflow responde adecuadamente a su pregunta.
t31os

Respuestas:

24

Al desarrollar un complemento, ¿deberían agruparse las funciones en una Clase para evitar conflictos de espacio de nombres?

Sí, pero ese es solo uno de los argumentos menores. De hecho, esa no es la naturaleza "verdadera" de una clase en OOAD .

¿El uso de clases crea gastos generales de rendimiento para PHP?

No, no notablemente. El mal diseño y / o el mal código escrito o la optimización prematura crean muchos más problemas de rendimiento que las características del lenguaje real.

Si hay un impacto en el rendimiento, ¿deberían los nombres de las funciones ser simplemente arreglados en su lugar?

Como está escrito, no hay éxito en el rendimiento. El código escrito incorrecto será más un éxito en el rendimiento que el código escrito bueno que tiene algunas líneas de código más pero no lo obliga a hacer cosas malas.


Línea de fondo:

Puede hacer un uso diferente de las clases para complementos. Puede usarlos para tener algún tipo de espacio de nombres y usarlos "solo" para funciones globales. La forma más directa de eso son las funciones de clase estática, el siguiente ejemplo de código muestra ambas, primero funciones globales, luego funciones de clase estática global:

/* global function */
function myplug_hook()
{
}

add_filter('the_hook', 'myplug_hook');


/* global static function */
class myplug
{
    public static function hook()
    {
    }
}

add_filter('the_hook', 'myplug::hook');

Este es solo un pequeño ejemplo que muestra que necesita escribir más para el gancho único. Además, muestra cómo funciona el espacio de nombres: puede reemplazar más fácilmente el nombre de una sola clase para cambiar el nombre de todas las funciones estáticas y luego buscar y reemplazar, lo myplug::que podría ser más difícil myplug_debido a los falsos positivos. Pero al final no hay mucha diferencia.

El punto clave es: funciones de clase estáticas Los documentos no son mucho más que funciones globales Documentos .

Y este ejemplo también muestra: el espacio de nombres está bien, pero con worpdress, el espacio de nombres se detiene al usar ganchos: la función de devolución de llamada está codificada, por lo tanto, el beneficio en el espacio de nombres usando la clase (un lugar para el nombre base, el nombre de clase) no ayuda cuando interviene su código con wordpress para los nombres de gancho.

El beneficio real comienza con el uso de instancias de clase reales y funciones no estáticas. Esto tiene el beneficio de que puede comenzar a utilizar los principios de OO y puede optimizar su código. Las funciones de clase estáticas son más un problema que una solución.

Entonces es más que solo azúcar sintáctico.

El punto clave es: Haz algo que te ayude a escribir el código con el que puedes lidiar y mantener fácilmente. No sobrevalores el rendimiento, es un error común. Más importante es que escriba código que sea fácil de leer y comprender, que simplemente haga lo que necesita. Quizás esta pregunta y respuesta sea útil para tener una visión más amplia en este contexto: Ayuda de Metabox personalizada múltiple .

Un enfoque común que tengo incluso con complementos más pequeños es hacer uso de una función auxiliar estática para crear instancias del complemento y el resto reside dentro de la instancia del complemento. Esto ayuda a encapsular la lógica principal del complemento y se beneficia del espacio de nombres con los ganchos, así como que los miembros privados pueden reutilizarse entre los ganchos, lo que no es posible con las funciones globales estándar. El siguiente código de ejemplo muestra el patrón:

<?php
/** Plugin Headers ... */

return MyPlugin::bootstrap(); 

class MyPlugin
{
    /** @var MyPlugin */
    static $instance;
    static public function bootstrap() {
        if (NULL === self::$instance) {
            self::$instance = new __CLASS__;
        }
        return self::$instance;
    }
    # ...
}

Este es un patrón común que uso para el archivo de complemento base. La clase de complemento, por un lado, representa el complemento para wordpress y, por otro lado, permite comenzar a usar paradigmas orientados a objetos para el propio código, que incluso puede estar completamente orientado a objetos (pero no necesariamente). Es una especie de controlador, que interactúa con toda la API de WordPress como la (s) solicitud (es).

Como muestra el ejemplo, se creará una instancia del complemento. Esto le permite hacer uso de los bienes comunes conocidos, como un Constructor Docs ( __construct) para inicializar el complemento real:

# ...
class MyPlugin
{
    # ...
    public function __construct()
    {
        add_filter('the_hook', array($this, 'hook'));
    }

    public function hook()
    {
    }
    # ...
}

En el momento en que se registra el enlace, este objeto de complemento ya se beneficia de su diseño: ha dejado de codificar la función de enlace real contra el nombre de clase del complemento concreto . Eso es posible debido al enlace de la clase a la instancia del objeto para la devolución de llamada. Suena complicado, solo digo: $this es el complemento. Se puede utilizar en devoluciones de llamada de enlace, compare los métodos de la Clase de registro como devoluciones de llamada de enlace .

Este patrón permite una interfaz más fácil con WordPress: la inyección se reduce a los nombres de los ganchos y a los datos que proporcionan. A continuación, puede comenzar a implementar directamente en esta clase de complemento o refactorizar su implementación contra ella, por lo que solo debe poner código en la clase de complemento que es lo mínimo para definir su interfaz de complementos contra WordPress, pero mantenga la lógica general a un lado de worpdress. Aquí es donde comienza la diversión y, muy probablemente, lo que cada autor del complemento quiere lograr a largo plazo.

Así que no programes con worpdress sino contra él. Como worpdress es bastante flexible, no existe una interfaz común o fácil de describir para programar. Una clase de complemento base puede asumir este rol, permitiéndole más flexibilidad para su propio código, lo que conducirá a un código más fácil y a un mejor rendimiento.

Por lo tanto, hay algo más que un beneficio para el espacio entre nombres. La mejor sugerencia que puedo dar es: pruébalo. No hay mucho que perderá, solo cosas nuevas por descubrir.

Probablemente notará diferencias después de haber pasado algunas actualizaciones más importantes de WordPress mientras mantiene su complemento compatible.

Advertencia : si su complemento se integra directamente con WordPress para hacer el trabajo, usar una o dos funciones públicas podría ser mejor para usted. Tome la herramienta adecuada para el trabajo.

hakre
fuente
1
Si las funciones de clase estáticas no son realmente diferentes de las funciones globales, y su objetivo es evitar conflictos de espacios de nombres, realmente no he entendido la necesidad (todavía) de cambiar a escribir complementos como clases. Además, estoy confundido por su función de arranque de ayuda. ¿Por qué no declarar el nuevo objeto como $ new_object = new MyClass () ;?
AlxVallejo
@AlxVallejo: para el espacio de nombres solo, no hay una verdadera necesidad (como escribí en la respuesta, los métodos de clase estáticos son más o menos lo mismo que las funciones globales). Por lo tanto, puede hacer el espacio de nombres usted mismo (el tipo de espacio de nombres anterior a PHP 5.3 que es). Entonces te diste cuenta de eso perfectamente bien. Similar a la función de arranque estático: técnicamente no es necesario, un simple también lo return $myPlugin = new MyPlugin(); hace. Sin embargo, para una imagen más grande, un simple nuevo podría no ser suficiente, compare el complemento de WordPress: ¿Cómo evito el "acoplamiento estrecho"? .
Hakre
9

Clases VS conjunto de funciones


Actuación

General: Afaik, no hay diferencia en el "rendimiento" entre clases y conjuntos de funciones.

Detalle:

  • Hay una gran diferencia si preguntas function_exists()vs. class_exists()como normalmente tienes muchas funciones (~ 1.800 (?) En wp core) versus clases (~ 100 (?) En wp core). Entonces hacer cosas "conectables" y, por lo tanto, cuestionar la existencia es una diferencia en el tiempo de ejecución.
  • Las clases ofrecen una gran ventaja sobre los conjuntos de funciones: puede evitar mucho más fácilmente llamarlo en una solicitud donde no lo necesita, y luego con las funciones. Solo tiene que hacer verificaciones condicionales para la clase y no para cada función. Entonces, si no lo necesita en cada carga de la página y puede evitar llamar a muchas declaraciones if / else, una función "funciona mejor".

Arquitectura - Cómo funcionan las cosas:

conjunto de funciones: en general, las funciones se ejecutan en la fila que usted llama. Por lo tanto, cada vez que llame a cosas, debe escribirlas nuevamente, si tiene que llamarlas más de una vez.

Clase: Hay diferentes enfoques para las clases. La clase más cercana a un conjunto de funciones es la clase "factory" ( wikipedia / google ). Imo es casi lo mismo que un conjunto de funciones, pero encapsulado en una clase. Pero también hay otros "tipos" de clases. Podría, por ejemplo, escribir un resumen o una clase de clase principal que amplíe con una clase secundaria. En un ejemplo del mundo real: Digamos que tienes una clase que crea algunos campos de texto estáticos. En su __construct()función, tiene una serie de escenarios como "left_column", "right_column" y "footer_field". Luego llamas a algo como $text_field = new TextFieldClass();crear una instancia de la clase. Y luego simplemente llamas $text_field->add( $case => 'left_column', 'case' => 'foo text' );y$text_field->add( $case => 'footer_field', 'case' => 'bar text' );. Entonces, todos sus condicionales y demás ya se han realizado cuando instancia la clase y solo las dos funciones de clase se habrían llamado al crear los campos de texto. En este escenario, podría haber ahorrado algunos ms de tiempo de ejecución.


Opinión personal

Si escribe sus clases sabiamente, tendrá una ventaja menor en el rendimiento. Pero tendrá una estructura bien organizada para trabajar. Hasta ahora nada espectacular. Pero si considera los siguientes casos de uso "divididos" para clases y funciones en un complemento, obtendrá mi punto final : la clase es interna, las funciones son API . Siempre que ofrezca API solo a través de funciones de uso público (que luego llaman a clases o funciones de clase), estará en el lado seguro desarrollando su complemento aún más. Obtuvo la libertad de cambiar la estructura interna o incluso las posibilidades de su complemento sin afectar a los usuarios en cualquier momento y en cualquier lugar.

Ejemplo:

// construction of object
if ( ! class_exists( 'WPSE_HelloWorld' ) )
{

class WPSE_HelloWorld
{
    function __construct( $args = array( 'text', 'html', 'echo' ) )
    {
        // call your object building procedures here
        $this->hello_world( 'text', 'html', 'echo' );
    }

    function hello_world( 'text', 'html', 'echo' )
    {
        $start_el = '<{$html}>';
        $end_el = '</{$html}>';
        if ( $echo )
        {
            return print "{$start_el}{$some}{$end_el}";
        }

        return "{$start_el}{$some}{$end_el}";
    }
} // END Class 

}

// API: public functions
function the_hello_world( $args( 'echo' => true ) )
{
    $new = new WPSE_HelloWorld();
    return $new->hello_world( $args );
}

function get_hello_world( array( $args( 'echo' => false) ) )
{
    $new = new WPSE_HelloWorld();
    return $new->hello_world( $args );
}

// then you can call it like get_the_title() or the_title(), which you know from the WP API:
// 'echo' is set to false per default:
$some_var = get_hello_world( array( 'text' => 'hello reader', 'html' => 'strong' ) );
# *returns* "<strong>hello reader</strong>"

// 'echo' is set to true per default:
the_hello_world( array( 'text' => 'hello reader', 'html' => 'strong' ) );
# *prints/echos* "<strong>hello reader</strong>"

Nota: Lea también el enlace @ t310s publicado en el comentario a la Q.

emperador
fuente
Por curiosidad, ¿por qué esperas que tu archivo de plugin se incluya más de una vez con WordPress?
Hakre
@hakre ¿Dónde dije exactamente esto? Sry, bastante cansado de la mamá.
Kaiser
1
@kaiser, supongo que @hakre se refiere a la if( ! class_exists )línea que tienes al principio?
jjeaton
1
@hakre Supongo que @kaiser está haciendo la class_existscomprobación no porque se pueda incluir más de una vez sino para evitar un conflicto con otra clase.
Michal Mau
Sí, me preguntaba acerca de class_exists.
Hakre
4

Es una elección puramente estilística por parte del autor del complemento. No hay una diferencia real en términos de velocidad.

Otón
fuente
1

Las clases no suelen ofrecer ningún beneficio en términos de rendimiento, pero muy rara vez tienen efectos negativos. Su beneficio real es aclarar el código y evitar conflictos de espacio de nombres.

Bainternet
fuente
Sin embargo, como mencionó @hakre, los conflictos de espacio de nombres no son realmente diferentes cuando se usan prefijos en funciones globales. El código "más limpio" y la prevención de conflictos de espacio de nombres son sinónimos en este caso, ¿no?
AlxVallejo
@AlxVallejo, supongo que sí :)
Bainternet
0

La mayoría de las veces, si usa funciones, pondrá el nombre del complemento en cada nombre de función, por lo que efectivamente, duplicará ese nombre una docena de veces si el complemento tiene una docena de funciones, lo cual es un poco pesado .

Con las clases, solo tendrías el nombre del complemento en el nombre de la clase probablemente una vez.

Además, puede usar la herencia u otras construcciones oo para implementar comportamientos de una manera muy limpia. Aquí hay un ex:

class animalplugin{
  //plugin functions...
  function talk(){print "animalnoise";}
}
class animalplugin_with_cat_mods extends abcplugin{
  //cat functions overrides
  function talk(){print "meow";}
}
if (iscat()){
  new animalplugin_with_cat_mods();
} else {
  new animalplugin();
}
Zeedre
fuente