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ő.