Hello monks.. I finally have something worth posting again (or maybe i'm just sleep deprived rambling).. and it goes a little something like this:
I'm working on a web game, which will hopefully be into it's beta testing phase soon so I can get my friends to waste their precious time again :) The game is a fairly large project, whose goal was to teach me some more about OO, DBs, and CGI funstuffs...
As I was looking through the code today, I noticed that one of my first objects, a little guy called ServerInfo didn't do much in the way of enforcing data hiding. Basically, ServerInfo's main duty is to read a configuration file and control the variables that come from it.
Well, for the longest time (up till about an hour ago) ServerInfo basically read the config file variables into a hash, and then I would refer to those variables like:
my $si = new ServerInfo;
$si->configure;
print "This is one of the variables: $si->{'VARIABLE_X'}";
As you can see, it's just a hash holding it. Nothing prevents someone from accidentally doing something like
$si->{'VAR_X'} = "No, you're not supposted to _assign_ me, you fool!". So I got thinking, how do i promote data hiding? I can't create a bunch of individual methods like
$si->variable_x(), since the data in the config file changes... So, onto my solution..
What I ended up doing, in my brainded-awake-for-24+-way-too-long-hours state was to modify the configure method just slightly, so that it keys the hash variables with an opening "i'm magic, don't touch me" underscore. So, my variable becomes
$si->{_VARIABLE_X}. And created another method, get() which appends an underscore to the beginning of it's argument and returns what is in the hash. Now I don't have to worry about someone doing
$si->get('VARIABLE_X') = "ha! you can't touch this".
So, what i'm wondering is, is my solution a good one? or will i wake up tomorrow, look at the code and wonder what the hell I was thinking? I figure making the variables harder to play with (Oh, sure, ppl can still assign to
$si->{_VARIABLE_X}, but they would have to do it intentionally, and we all know intentionally messing with magic variables is a no-no in most instances) is a good thing, but now I have to access a function rather than just accessing the hash.. I'm not sure yet if there will be much in the way of a performance loss... Any other thoughts on how to handle something like this? code critique?
Thanks monks..
-Syn0
And here's the code and a bit of the configuration file...
(oh, and this was also my first toying with POD, fun)
# ====================================================================
+==
# ServerInfo.pm
# Server Information object
#
# Copyright (C) 2001 Art C. Pacheco (Synapse0) All rights reserved.
#
# ====================================================================
+====
package ServerInfo;
use strict;
# ====================================================================
+==
# /* -----------------------------------------------------------------
+-- */
sub new {
my $class = shift;
my $self = {};
bless($self, $class);
# Grab the name of the calling executable.
my $x = $0;
$x =~ s#.+/##;
$self->script("$x?");
return $self;
}
# ====================================================================
+==
# script(scalar)
# sets/returns name of script
# /* -----------------------------------------------------------------
+-- */
sub script {
my $self = shift;
$self->{_script} = shift if @_;
return $self->{_script};
}
# ====================================================================
+==
# assign(KEY, VALUE[, VALUE, VALUE, ...])
# assigns key->value pairs to ServerInfo hash
# /* -----------------------------------------------------------------
+-- */
sub assign {
my $self = shift;
my $field = shift;
my $item;
$field = "_$field";
if (@_ > 1) {
# we were pushed an array
while (@_) {
$item = shift;
push(@{$self->{$field}}, $item);
}
}
else {
# we were pushed a single item
$item = shift;
$self->{$field} = $item;
}
}
# ====================================================================
+==
# configure()
# reads in configuration file ( ./_config.inf ) and assign()s the
# key->value pairs specified
# /* -----------------------------------------------------------------
+-- */
sub configure {
my $self = shift;
my($cfg_var, $val) = ('','');
if (open(FILE, "< _config.inf")) {
while (<FILE>) {
# skip anything that's not a directive, untaint
next unless my($line) = ($_ =~ /^(\w.*)/);
chomp($line);
($cfg_var, $val) = split(/\s+/, $line, 2);
$val =~ s/{(.*?)}/$self->get($1)/eg; # resolve any {vars}
if ($val =~ /^\@ (.*)/) {
# $val holds a list
$self->assign($cfg_var, split(/\s+/, $1));
}
else {
$self->assign($cfg_var, $val);
}
}
close(FILE);
}
else {
# do something about open error
print "ACK!! NOOOOOOOOOOOO : $!\n\n\n\n";
}
# debug
$self->assign('DEBUG', -e "$self->{_BaseDir}/DEBUG" ? 1 : 0);
}
# ====================================================================
+==
# get(scalar)
# retrieves a value (scalar or list) assigned by assign()
# /* -----------------------------------------------------------------
+-- */
sub get {
my $self = shift;
my $key = shift;
return $self->{"_$key"};
}
# /* -----------------------------------------------------------------
+-- */
1;
__END__
=head1 NAME
ServerInfo - Server information object
=head1 SYNOPSIS
# create a new server object and execute configuration
use _Mod::ServerInfo;
my $si = new ServerInfo;
$si->configure;
# access a configuration variable
# used to be $email_link = $si->{AdminEmailLink}
# but now it's safer
$email_link = $si->get('AdminEmailLink');
=head1 DESCRIPTION
ServerInfo encapsulates configuration information obtained from the _c
+onfig
file in the root directory.
=head2 OBJECT METHODS
=item $si = ServerInfo->new()
Creates a new ServerInfo object. The constructor initializes the
script() method described next.
=item $si->script([scalar scriptname])
If given an argument, script() will set the name of the current script
+.
If called without an argument, script() will return the script name.
script() is initialized when a new ServerInfo object is created.
=item $si->assign(scalar key, scalar/list value)
Takes two (or more for an list) arguments and creates a hash,
assigning the value(s) to the key.
=over 4
=item
example:
$si->assign('KEY', 'VALUE'[, 'VALUE', 'VALUE', ...])
=back
=item $si->configure()
Configuration method. Reads in data from the configuration file
B<_config.inf> which lives in the same directory as the ServerInfo mod
+ule.
configure() reads in the data and splits the info at the first whitesp
+ace,
using the lefthand data for the key, and the righthand data for the va
+lue
(which can be a list). It uses assign() to place the data into a hash,
accessible by the ServerInfo object.
=item $si->get(scalar key)
Retrieval method. Returns a value assigned by the assign() method
=head1 AUTHOR INFORMATION
Copyright 2000-2001, Art Pacheco. All rights reserved.
=cut
config file:
BaseDir /home/tech/htdocs/arena
BaseURL http://dev/~tech/arena
#
# Intro script (index script)
#
IntroURL {BaseURL}/arenaintro.cgi?
IntroEXE {BaseDir}/arenaintro.cgi
#
# Some Default Colors
# DefBody: bgcolor text link vlink alink
DefBody @ #000000 #99c68e #99c68e #99c68e #bcc7c7
# Table background color
TableBgColor #333333
# Hilighted text
hilight #c9e6be
# bold text
bold #c7aa7d
# undefined color
UndefColor #555555
#
# Battle colors
# Char basic attack color
ChBasAtt #559955
OppBasAtt #00aaaa
ChAttHit #dddd55
OppAttHit #dd0055
ChMiss #77aa77
OppMiss #22bb99
ChArmAbsorb #658978
OppArmAbsorb #008888
ChSpecAtt #bbbb33
OppSpecAtt #bb0033
ChCounter #88cc88
OppCounter #33dddd