Ich entwerfe eine Klasse, die ein hochkomplexes Objekt mit einer Tonne (50+) von meist optionalen Parametern definiert, von denen viele Standardwerte haben würden (zB: $type="foo"; $width="300"; $interactive = false;
). Ich versuche herauszufinden, wie ich den Konstruktor und die Instanz-/Klassenvariablen am besten einrichten kann, um Folgendes tun zu können:
- machen es einfach, die Klasse zu verwenden
- Machen Sie es einfach, die Klasse automatisch zu dokumentieren (z. B. mit phpDocumentor).
- Code dies elegant
In Anbetracht des oben Gesagten möchte ich dem Konstruktor nicht eine Menge Argumente übergeben. Ich werde ihm einen einzelnen Hash übergeben, der die Initialisierungswerte enthält, z. $foo = new Foo(array('type'=>'bar', 'width'=>300, 'interactive'=>false));
In Bezug auf das Programmieren der Klasse habe ich immer noch das Gefühl, ich hätte lieber …
class Foo {
private $_type="default_type";
private $_width = 100;
private $_interactive = true;
...
}
… weil ich glaube, dass dies die Dokumentationsgenerierung erleichtern würde (Sie erhalten die Liste der Klasseneigenschaften, die den API-Benutzer wissen lässt, mit welchen ‘Optionen’ er arbeiten muss), und es “fühlt” sich wie der richtige Weg an es.
Aber dann stoßen Sie auf das Problem, die eingehenden Parameter im Konstruktor den Klassenvariablen zuzuordnen, und ohne die Symboltabelle auszunutzen, geraten Sie in einen “Brute-Force” -Ansatz, der für mich den Zweck zunichte macht (obwohl ich offen für andere bin Meinungen). Z.B:
function __construct($args){
if(isset($args['type'])) $_type = $args['type']; // yuck!
}
Ich habe überlegt, eine einzelne Klassenvariable zu erstellen, die selbst ein assoziatives Array ist. Das Initialisieren wäre dann wirklich einfach, zB:
private $_instance_params = array(
'type' => 'default_type',
'width' => 100,
'interactive' => true
);
function __construct($args){
foreach($args as $key=>$value){
$_instance_params[$key] = $value;
}
}
Aber das scheint, als würde ich native Funktionen wie private Klassenvariablen nicht nutzen, und es scheint, als würde die Dokumentationsgenerierung mit diesem Ansatz nicht funktionieren.
Danke, dass Sie bis hierhin gelesen haben; Ich frage hier wahrscheinlich viel, aber ich bin neu in PHP und suche wirklich nur nach dem idiomatischen / eleganten Weg, dies zu tun. Was sind Ihre Best Practices?
Nachtrag (Details zu dieser bestimmten Klasse)
Es ist sehr wahrscheinlich, dass diese Klasse versucht, zu viel zu tun, aber es ist eine Portierung einer alten Perl-Bibliothek zum Erstellen und Verarbeiten von Formularen. Es gibt wahrscheinlich eine Möglichkeit, die Konfigurationsoptionen aufzuteilen, um Vererbung und Polymorphismus zu nutzen, aber es kann tatsächlich kontraproduktiv sein.
Auf Wunsch hier eine Teilauflistung einiger Parameter (Perl-Code). Sie sollten sehen, dass diese Unterklassen nicht sehr gut zugeordnet werden können.
Die Klasse hat sicherlich Getter und Setter für viele dieser Eigenschaften, sodass der Benutzer sie überschreiben kann; Das Ziel dieses Beitrags (und etwas, das der ursprüngliche Code gut macht) ist es, eine kompakte Möglichkeit zum Instanziieren dieser Form-Objekte mit den erforderlichen Parametern bereitzustellen, die bereits festgelegt sind. Es sorgt tatsächlich für sehr gut lesbaren Code.
# Form Behaviour Parameters
# --------------------------
$self->{id}; # the id and the name of the <form> tag
$self->{name} = "webform"; # legacy - replaced by {id}
$self->{user_id} = $global->{user_id}; # used to make sure that all links have the user id encoded in them. Usually this gets returned as the {'i'} user input parameter
$self->{no_form}; # if set, the <form> tag will be omitted
$self->{readonly}; # if set, the entire form will be read-only
$self->{autosave} = ''; # when set to true, un-focusing a field causes the field data to be saved immediately
$self->{scrubbed}; # if set to "true" or non-null, places a "changed" radio button on far right of row-per-record forms that indicates that a record has been edited. Used to allow users to edit multiple records at the same time and save the results all at once. Very cool.
$self->{add_rowid}; # if set, each row in a form will have a hidden "rowid" input field with the row_id of that record (used primarily for scrubbable records). If the 'scrubbed' parameter is set, this parameter is also automatically set. Note that for this to work, the SELECT statement must pull out a unique row id.
$self->{row_id_prefix} = "row_"; # each row gets a unique id of the form id="row_##" where ## corresponds to the record's rowid. In the case of multiple forms, if we need to identify a specific row, we can change the "row_" prefix to something unique. By default it's "row_"
$self->{validate_form}; # parses user_input and validates required fields and the like on a form
$self->{target}; # adds a target window to the form tag if specified
$self->{focus_on_field}; # if supplied, this will add a <script> tag at the end of the form that will set the focus on the named field once the form loads.
$self->{on_submit}; # adds the onSubmit event handler to the form tag if supplied
$self->{ctrl_s_button_name}; # if supplied with the name of the savebutton, this will add an onKeypress handler to process CTRL-S as a way of saving the form
# Form Paging Parameters
# ----------------------
$self->{max_rows_per_page}; # when displaying a complete form using printForm() method, determines the number of rows shown on screen at a time. If this is blank or undef, then all rows in the query are shown and no header/footer is produced.
$self->{max_pages_in_nav} = 7; # when displaying the navbar above and below list forms, determines how many page links are shown. Should be an odd number
$self->{current_offset}; # the current page that we're displaying
$self->{total_records}; # the number of records returned by the query
$self->{hide_max_rows_selector} = ""; # hide the <select> tag allowing users to choose the max_rows_per_page
$self->{force_selected_row} = ""; # if this is set, calls to showPage() will also clear the rowid hidden field on the form, forcing the first record to be displayed if none were selected
$self->{paging_style} = "normal"; # Options: "compact"
Wir können uns natürlich auf eine längere Debatte über den Programmierstil einlassen. Aber ich hoffe, dass ich es vermeiden kann, zum Wohle aller Beteiligten! Hier (wieder Perl-Code) ist ein Beispiel für die Instanziierung dieses Objekts mit einem ziemlich umfangreichen Parametersatz.
my $form = new Valz::Webform (
id => "dbForm",
form_name => "user_mailbox_recip_list_students",
user_input => \%params,
user_id => $params{i},
no_form => "no_form",
selectable => "checkbox",
selectable_row_prefix => "student",
selected_row => join (",", getRecipientIDsByType('student')),
this_page => $params{c},
paging_style => "compact",
hide_max_rows_selector => 'true',
max_pages_in_nav => 5
);
Das hört sich an, als würde die Klasse zu viel tun. Können Sie näher darauf eingehen, was diese Klasse tun soll, und vielleicht einige weitere oder alle der 50 Eigenschaften auflisten?
– Gordon
11. Mai 2011 um 16:14 Uhr
Was ist der Nachteil daran, diese öffentlichen Mitglieder zu machen? Müssen sie nach dem Bau repariert werden und stellen Sie keine anderen Mittel zur Verfügung, um die Werte zu ändern?
– Mel
11. Mai 2011 um 17:00 Uhr
Da Sie Ihre Frage nicht aktualisiert haben, um die angeforderten Informationen einzuschließen, verlinke ich Sie nur auf die Großer Klassencode-Geruch. Ich ermutige Sie, es zu lesen und die vorgeschlagenen Refactorings anzuwenden.
– Gordon
11. Mai 2011 um 17:24 Uhr
@Gordon – locker Kumpel, gib einem Typen Zeit! In Bezug auf das Large-Class-Syndrom haben Sie hier wahrscheinlich Recht – ich hoffe, dass meine Erklärungen im Anhang diese Bedenken ansprechen (wenn nicht rechtfertigen). Insbesondere erleichtert die Klassenextraktion oder Unterklassenextraktion nicht die Art von “Abkürzungs”-Instanziierung, die ich dem Benutzer der API zur Verfügung stellen möchte. Hoffe das macht Sinn. Danke für den Link, aber die tolle Seite hatte ich vergessen.
– Tom Auger
11. Mai 2011 um 17:54 Uhr
@Tom Danke für das Update. Die Klasse macht definitiv zu viel. Dies ist leicht an der Aufteilung der Eigenschaften in Form Behaviour und Form Paging zu erkennen. Obwohl es keine “Abkürzung” ist, würde ich dennoch versuchen, es in kleinere Teile umzugestalten, um die Komplexität der verschiedenen Komponenten herauszunehmen. Zum Instanziieren können Sie ein Factory- oder Builder-Muster verwenden.
– Gordon
11. Mai 2011 um 18:15 Uhr