Az üzleti logika megvalósítása során a gyakran használt algoritmus-részeket függvényekbe, még inkább osztályok metódusaiba gyűjtjük.
Az újrahasznosítás során az osztály metódusainak (szolgáltatásainak) hívásával "egyszerűsítjük" a kódunkat (lévén egy több lépéses algoritmus helyett csupán egy hívást illesztünk a kódba).
A módszer lényege, hogy csökkentsük az átadott paraméterek számát: csak azt hagyjuk benne, ami feltétlenül szükséges a feladat elvégzéséhez.
Célunk egy érthetőbb és áttekinthetőbb kód létrehozása az igények alapján.
Az átalakítást célszerű olyan metódussal kezdeni, amit:
- gyakran hívunk,
- módosítani, továbbfejleszteni kell,
- összetett feladatot lát el, amiből egy/több feladatot másik helyen is fel kell használnunk,
- stb.
Példa, kép feliratozása
Az alábbi PHP példa során mutatom be a módszer lépéseit.
(Nem cél egy teljes, mindenre kiterjedő megoldás bemutatása, mivel az átalakítás lehetőségei, lépései nagymértékben függnek az igényektől, a metódus és a környezet feladatától, stb.)
Az üzleti logika alapján JPG formátumú képre kell szöveget írnunk, a kép bal és felső szélétől adott, egyenlő távolságra.
Az előkészületek érdekében vegyünk egy JPG állományt (mintakep.jpg), illetve egy fontot (arial.ttf). (A példa érdekében mindkét állományt a C gyökerében helyeztem el.)
(Hogy ne érjen minket meglepetés, célszerű a magyar ABC minden betűjét tartalmazó szöveget írni a képre. A "Hello world!" itt most kevésbé hasznos. :)
A kezdeti forrásunk.
$image = imagecreatefromjpeg( 'c:\mintakep.jpg' ); $colorWhite = imagecolorallocate( $image, 255, 255, 255 ); $font = 'c:\arial.ttf'; $text = 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP'; imagettftext( $image, 12, 0, 4, 16, $colorWhite, $font, $text ); imagejpeg( $image,'c:\mintakep_output.jpg' );
(Mindenkitől elnézést a minták esetleg elcsúszó tabuláltsága miatt. Kiemeléssel igyekeztem szemléltetni a kód módosulásait, sajnos ez kód-struktúra kárára ment. Ennyit sikerült kihozni a WYSIWYG/HTML szerkesztőből.)
A fenti példában az imagettftext alap PHP függvény végzi a szöveg kiírását a képre. Látható, hogy a paraméterek átadása nem túl beszédes.
A nevesített paraméterekről van sejtésünk, milyen feladatot látnak el. A többi esetében viszont csak tippelhetünk; bővebb felvilágost csak a manual ad.
Az igények alapján a paraméterek nagy részére nincs szükségünk.
(További probléma, hogy a fenti megoldás egyáltalán nem rugalmas. Másik kép feliratozása, több szöveg elhelyezése - bár megoldható -, mégis macerás. Nem tudjuk újrahasznosítani a kódot, nehezen tesztelhető, stb.)
Osztály létrehozása
Első lépésben készítsünk egy osztályt (pl. myImage néven), az imagettftext-et pedig helyezzük a write metódusba; ez fogja elvégezni a képre írást.
class myImage {
public function write( &$image, $size, $angle, $x, $y, $color, $fontfile, $text ) {
imagettftext( $image , $size, $angle, $x, $y, $color, $fontfile, $text );
}
}
$myImage = new myImage();
$image = imagecreatefromjpeg( 'c:\mintakep.jpg' );
$colorWhite = imagecolorallocate( $image, 255, 255, 255 );
$fontfile = 'c:\arial.ttf';
$text = 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP';
$myImage -> write( $image, 12, 0, 4, 16, $colorWhite, $fontfile, $text );
imagejpeg( $image, 'c:\mintakep_output.jpg' );
Az új metódus paramétereiben használjunk beszédes paraméter neveket. (Javaslom, hogy vegyük alapul a PHP manualban használt elnevezéseket. További elnevezési ajánlások: változó, metódus, osztály.)
Attribútumok számának további csökkentése
Emeljük be az osztályba azokat az attribútumokat, amiket - jelen példa során - nem akarunk módosítani, ilyen pl. a betűtípus is.
Mivel ezek az attribútumok már az osztály részei, fel tudjuk használni az osztályon belül; így egy paraméterrel már rövidebb a hívás és a write metódus is.
class myImage {
const fontfile = 'c:\arial.ttf';
public function write( &$image, $size, $angle, $x, $y, $color, $text ) {
imagettftext( $image , $size, $angle, $x, $y, $color, self::fontfile, $text );
}
}
$myImage = new myImage();
$image = imagecreatefromjpeg( 'c:\mintakep.jpg' );
$colorWhite = imagecolorallocate( $image, 255, 255, 255 );
$text = 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP';
$myImage -> write( $image, 12, 0, 4, 16, $colorWhite, $text );
imagejpeg( $image,'c:\mintakep_output.jpg' );
Egyszerűsítések az igények alapján
Annak érdekében, hogy már a példányosításkor - kötelezően - meg tudjunk adni egy forrás képet, a konstruktorba helyezzük ennek a lehetőségét.
Mivel sem a felirat színét sem pedig a szöget nem akarjuk módosítani, így ezek megadását is kivehetjük.
class myImage {
const fontfile = 'c:\arial.ttf';
public $image = null;
public $color = null;
public $angle = 0;
protected $imageFilePathSource = null;
function __construct( $imageFilePathSource ) {
$this -> imageFilePathSource = $imageFilePathSource;
$this -> image = imagecreatefromjpeg( $this -> imageFilePathSource );
$this -> color = imagecolorallocate( $this -> image, 255, 255, 255 );
}
public function write( &$image, $size, $x, $y, $text ) {
imagettftext( $image , $size, $this -> angle, $x, $y, $this -> color, self::fontfile, $text );
}
}
$myImage = new myImage( 'c:\mintakep.jpg' );
$text = 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP';
$myImage -> write( $myImage -> image, 12, 4, 16, $text );
imagejpeg( $myImage -> image,'c:\mintakep_output.jpg' );
További egyszerűsítések
Mivel nem akarunk több különféle méretű feliratot készíteni - de egy alap méretet azért mégis csak szeretnénk megadni -, a lehetőséget a konstruktorban adjuk meg, ugyanakkor egy default szöveg méretet is definiálunk, ha a példányosításkor nem adnánk meg semmilyen értéket.
Eltávolítottam a $text változót is, betettem a hívásba. Itt feleslegesnek tűnik egy külön változót fenntartani a szövegnek.
class myImage {
const fontfile = 'c:\arial.ttf';
public $image = null;
public $color = null;
public $angle = 0;
const sizeDefault = 12; public $size = self::sizeDefault;
protected $imageFilePathSource = null;
function __construct( $imageFilePathSource, $size = null ) {
$this -> imageFilePathSource = $imageFilePathSource;
$this -> image = imagecreatefromjpeg( $this -> imageFilePathSource );
$this -> color = imagecolorallocate( $this -> image, 255, 255, 255 );
if( !is_null( $size ) ) {
$this -> size = $size; }
}
function write( &$image, $x, $y, $text ) {
imagettftext( $image , $this -> size, $this -> angle, $x, $y, $this -> color, self::fontfile, $text ); }
}
$myImage = new myImage( 'c:\mintakep.jpg', 12 );
$myImage -> write( $myImage -> image, 4, 16, 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP' );
imagejpeg( $myImage -> image,'c:\mintakep_output.jpg' );
A kép mentése
Már csak a kép mentése van hátra, amit a kimeneti állománynév megadásával tudunk meghívni.
class myImage {
const fontfile = 'c:\arial.ttf';
public $image = null;
public $color = null;
public $angle = 0;
const sizeDefault = 12;
public $size = self::sizeDefault;
protected $imageFilePathSource = null;
function __construct( $imageFilePathSource, $size = null ) {
$this -> imageFilePathSource = $imageFilePathSource;
$this -> image = imagecreatefromjpeg( $this -> imageFilePathSource );
$this -> color = imagecolorallocate( $this -> image, 255, 255, 255 );
if( !is_null( $size ) ) {
$this -> size = $size;
}
}
public function write( $x, $y, $text ) {
imagettftext( $this -> image , $this -> size, $this -> angle, $x, $y, $this -> color, self::fontfile, $text );
}
public function save( $imageFilePathDestination ) { imagejpeg( $this -> image, $imageFilePathDestination );
}
}
$myImage = new myImage( 'c:\mintakep.jpg', 12 );
$myImage -> write( 4, 16, 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP' );
$myImage -> save( 'c:\mintakep_output.jpg' );
Láthatóságok aktualizálása
Ezzel tulajdonképpen megvagyunk, tiszta kódot kaptunk a refactoring lépéseivel.
Az osztályunk ugyanakkor nem teljesíti az egységbezárás feltételeit, mivel többet mutat magából, mint amennyit feltétlenül szükséges lenne. A változók kívülrők elérhetőek, módosíthatóak.
Rejtsük el a változókat, hogy az osztály tudta és felügyelete nélkül ne lehessen őket módosítani. Célszerű ilyenkor a legszigorúbban eljárni.
class myImage {
const fontfile = 'c:\arial.ttf';
protected $image = null;
protected $color = null;
protected $angle = 0;
const sizeDefault = 12;
protected $size = self::sizeDefault;
protected $imageFilePathSource = null;
function __construct( $imageFilePathSource, $size = null ) {
$this -> imageFilePathSource = $imageFilePathSource;
$this -> image = imagecreatefromjpeg( $this -> imageFilePathSource );
$this -> color = imagecolorallocate( $this -> image, 255, 255, 255 );
if( !is_null( $size ) ) {
$this -> size = $size;
}
}
public function write( $x, $y, $text ) {
imagettftext( $this -> image , $this -> size, $this -> angle, $x, $y, $this -> color, self::fontfile, $text );
}
public function save( $imageFilePathDestination ) {
imagejpeg( $this -> image, $imageFilePathDestination );
}
}
$myImage = new myImage( 'c:\mintakep.jpg', 12 );
$myImage -> write( 4, 16, 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP' );
$myImage -> save( 'c:\mintakep_output.jpg' );
Adott távolságra a szélektől
Ebben a lépésben valósítottam meg, hogy csak a bal felső szélektől adott távolságra tudjunk írni: elrejtem a write metódust, és writeLeftTop néven bevezetek egy új metódust.
Annak érdekében, hogy azért mégis csak egyszerűen legyen lehetőségünk több képet is feliratozni, a konstruktorban található kódrészletet metódusba emeltem ki. (Ebben a példában kelleni fog egy mintakep2.jpg is.)
(Hogy az átalakítás bemutatását ne zavarják, a végére hagytam a kommentezést.)
class myImage {
const fontfile = 'c:\arial.ttf';
protected $image = null;
protected $color = null;
protected $angle = 0;
const sizeDefault = 12;
protected $size = self::sizeDefault;
protected $imageFilePathSource = null;
/**
* A forrás állomány megadása a teljes elérési úttal.
*
* @param string Az állomány teljes elérési útja.
*/
public function setImageFilePathSource( $imageFilePathSource = null ) {
if( is_null( $imageFilePathSource ) ) {
return;
}
$this -> imageFilePathSource = $imageFilePathSource;
$this -> image = imagecreatefromjpeg( $this -> imageFilePathSource );
$this -> color = imagecolorallocate( $this -> image, 255, 255, 255 );
}
/**
*
* @param string A forrás állomány teljes elérési útja.
* @param int A szöveg mérete
*/
function __construct( $imageFilePathSource = null, $size = null ) {
$this -> setImageFilePathSource( $imageFilePathSource );
if( !is_null( $size ) ) {
$this -> size = $size;
}
}
/**
* A szöveg méretének beállítása
*
* @param int Méret
*/
public function setSize( $size ) {
$this -> size = $size;
}
/**
* Szöveg írása a képre; a bal felső sarok koordinátája: (x, y) = (0, 0)
*
* @param int A szöveg megjelenésének X pozíciója
* @param int A szöveg megjelenésének Y pozíciója
* @param string A szöveg
*/
protected function write( $x, $y, $text ) {
imagettftext( $this -> image , $this -> size, $this -> angle, $x, $y, $this -> color, self::fontfile, $text );
}
/**
* Kép bal felső sarkába szöveg írása
*
* @param string A szöveg
* @param int Távolság a kép széleitől
*/
public function writeLeftTop( $text, $delta = 0 ) {
$this -> write( $delta, $this -> size + $delta, $text );
}
/**
* Kép mentése a megadott kimeneti állományba
*
* @param string A kimeneti állomány teljes elérési útja
*/
public function save( $imageFilePathDestination ) {
imagejpeg( $this -> image, $imageFilePathDestination );
}
}
$myImage = new myImage();
$myImage -> setSize( 12 );
$myImage -> setImageFilePathSource( 'c:\mintakep.jpg' );
$myImage -> writeLeftTop( 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP', 4 );
$myImage -> save( 'c:\mintakep_output.jpg' );
$myImage -> setImageFilePathSource( 'c:\mintakep2.jpg' );
$myImage -> writeLeftTop( 'árvíztűrő tükörfúrógép ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP', 4 );
$myImage -> save( 'c:\mintakep_output2.jpg' );
Igaz, hogy a kezdeti 9 sorból majd 100 soros kód lett, de a kapott előnyök - azt gondolom -, kárpótolják a befektetett munkát.
A refactoringnak alávetett kód áttekinthetőbb, a változó/bővülő igyényekre rugalmasabban felkészíthető.

