Monday, February 3, 2014

Java සමග Software Design Patterns (5 කොටස | Chain of Responsibility)

මීට පෙර ලිපි වලින් අපි design patterns කිහිපයක් පිලිබඳ අධ්‍යයනය කලා එහිදී design patters හි වැදගත් කම, විවිධ ආකාරයන් ගැන යම් අවබෝධයක් ලබාගන්නට අපිට හැකිවුනා. මෙම ලිපියෙන් අපි සාකච්ඡා කිරීමට බලාපොරොත්තු වන්නේ behavioral pattern එකක් වන "Chain of Responsibility" පිළිබඳවයි.

මේ දිනවල මා හට මෙම pattern එක ප්‍රායෝගිකව භාවිතා කිරීමට අවස්ථාවක් උදාවුනා. එම නිසා එම අවස්ථාව මෙම පාඩමේ උදාහරණයක් ලෙස භාවිතා කිරීමෙන් වඩාත් හොඳ අවබෝධයක් එමගින් ලබාගත හැකිවේවි.

ගැටලුව : දෙන ලද ඩේටා බේස් එකක බැකප් එකක්(dump) ලබාගෙන එය Dropbox නම් online storage එකට අප්ලෝඩ් කල යුතුය. ඊට අමතරව එම බැකප් එක/ඩම්ප් එක ඔන්ලයින් දත්ත ගබඩාවේ සේව් කිරීමට පෙර zip කිරීම කල යුතුය. 

මේ සඳහා එක method එකක් හෝ එක class භාවිතයෙන් වුවත් විසඳුමක් ලබාදිය හැකි බව යමෙකුට සිතන්න පුලුවන්. නමුත් ඉහත අවශ්‍යතාව සඳහා විසඳුමක් ලබාදීමට පෙර පහත කරුණු කෙරෙහි මා විසින් විශේෂ සැලකිල්ලක් දැක්වූවා.

  • ඉදිරියේදී zip කිරීම වෙනුවට tar.gz, 7zip වැනි ආකාරයක් යොදාගැනීමට තීරණය වුවහොත් ඒ සඳහා වැඩසටහනේ සුලු වෙනසක් කිරීම මගින් වඩා පැහැදිලිව එය සිදුකල හැකි වීම
  • එමෙන්ම  Dropbox වෙනුවට වෙනත් online storage එකක්(ftp, sky drive..) යොදාගැනීමට යෝජනා වුවහොත් එයද ඉක්මනින් සහ පැහැදිලිව කිරීමේ හැකියාව තිබීම
  • සමස්ථ ක්‍රියාවලිය එකිනෙකට වෙනස් උප ක්‍රියාවලි කිහිපයකට බෙදා (වෙන වෙනම classes) ඒවා අතර coupling එකද අඩුකිරීමට හැකිවීම
  • මා විසින් ලියන ලද කේතකොටස තවත් අයෙකුට පහසුවෙන් වටහාගැනීමට මෙන්ම ඉදිරියේදී මෙම ක්‍රියාවලියේ යම් වෙනසක් හෝ අලුතෙන් යම් පියවරක් එකතු කිරීමට අවශ්‍ය වුවහොත් විශාල වෙනස් කිරීමකින් තොරව එය ඉතා පහසුවෙන් සිදුකල හැකි වීම.
මුලින් ලබාදී ඇති requirement එකෙහි ඉහත අවශ්‍යතා සෘජුව කියවුනේ නැතත් එවන් තත්වයන්ටද මුහුන දිය හැකිනම් වඩාත් හොඳ නිසා මේ සඳහා design pattern එකක් භාවිතා කර විසඳුමක් ලබාදීම වඩාත් යෝග්‍යයි. ඒ අනුව chain of responsibility මේ සඳහා යෝග්‍ය pattern එකක් ලෙස හඳුනා ගන්න පුලුවන්. 

එම pattern එක ක්‍රියාත්මක වන්නේ pipeline එකක් ආකාරයෙන්. පහත රූපසටහන ආධාරයෙන් එය වඩාත් පහසුවෙන් වටහාගන්නට පුලුවන්.
අප තෝරාගත් උදාහරණයට අනුව පහත අනුපිලිවෙලට එම ක්‍රියාවලිය සිදුවිය යුතුය
  1. ඩේට බේස් dump එක සෑදීම
  2. එම dump ෆයිල් එක zip format එකෙන් compress කිරීම
  3. Dropbox එක වෙත එම zip කරන ලද ෆයිල් එක අප්ලෝඩ් කිරීම
  4. ඉහත ක්‍රියාවලියේදී තාවකාලිකව සාදන ලද සියලු ෆයිල් මකාදැමීම හා ක්‍රියාවලිය අවසන් කිරීම.
design pattern එක සඳහා class diagram එක පහත පරිදි වේ.
එම design pattern එක යොදාගනිමින් අදාල ගැටලුව විසඳීම සඳහා අපිට පහත පරිදි design එකක් සකස් කල හැක. මා විසින් Handler එක සඳහා තෝරාගන්නා ලද්දේ abstract class එකකි. අවස්ථානුකූලව ඒ සඳහා interface එකක් වුවද යොදාගත හැකිය.
දැන් අපි මෙය ජාවා යොදාගෙන implement කරන ආකාරය සලකා බලමු.
DBBackupHandler.java
package responsibility;

import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public abstract class DBBackupHandler {

    private DBBackupHandler nextHandler;

    protected DBBackupHandler getNextHandler() {
        return nextHandler;
    }

    protected void setNextHandler(DBBackupHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    abstract void processRequest(BackupRequest request) throws HandlerFaliureException;
}
මෙම class එක අදාල pattern එකෙහි හරය ලෙස හැඳින්වුවද වරදක් නැත. අනෙකුත් සියලුම sub task මෙම class එක extend කිරීම අනිවාර්ය වේ. nextHandler property එක මගින් chain එකෙහි ඊලඟ පුරුක(next responsibility) සඳහා reference එකක් පවත්වා ගනී.

ඊලඟට වැදගත් වනුයේ BackupRequest නම් class එකයි. එය data model එකක් ලෙස ක්‍රියාකරයි. responsibility chain එකෙහි ගමන් කරන request එක නියෝජනය කරන්නේ මෙම ක්ලාස් එකයි. මෙහි request එකේ state එක සටහන් කර ගැනීම සිදුකරයි. සියලුම responsibilities හරහා ගමන් කරනුයේ මෙම ඩේටා මොඩ්ල් එකයි.
BackupRequest.java
package responsibility;

import java.io.File;

/**
 *
 * @author kanishka
 */
public class BackupRequest {

    private BackupStatus status;
    private String dbName;
    private File dumpFile;
    private File zipedDumpFile;

    public BackupStatus getStatus() {
        return status;
    }

    public void setStatus(BackupStatus status) {
        this.status = status;
    }

    public String getDbName() {
        return dbName;
    }

    public void setDbName(String dbName) {
        this.dbName = dbName;
    }

    public File getDumpFile() {
        return dumpFile;
    }

    public void setDumpFile(File dumpFile) {
        this.dumpFile = dumpFile;
    }

    public File getZipedDumpFile() {
        return zipedDumpFile;
    }

    public void setZipedDumpFile(File zipedDumpFile) {
        this.zipedDumpFile = zipedDumpFile;
    }
}
BackupStatus.java
package responsibility;

/**
 *
 * @author kanishka
 */
public enum BackupStatus {

    INITIALIZED, DUMPED, ZIPED, UPLOADED, COMPLETED
}
දැන් අපි බලමු අදාල කාර්යභාරය ඉටුකරන processes නැතහොත් handlers/responsibilities කුමන ආකාරයෙන් පවතිනවාද කියල.
අපගේ උදාහරණයේ මුල්ම responsibility එක වන්නේ database dump කිරීමට අදාල class එකයි.
DumpProcessHandler.java
package responsibility;

import java.io.File;
import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class DumpProcessHandler extends DBBackupHandler {

    public DumpProcessHandler(DBBackupHandler handler) {
        setNextHandler(handler);
    }

    @Override
    void processRequest(BackupRequest request) throws HandlerFaliureException {
        if (request.getStatus() == BackupStatus.INITIALIZED) {
            System.out.println("Successfully dumped database : " + request.getDbName());
            request.setStatus(BackupStatus.DUMPED);
            request.setDumpFile(new File("001.sql"));

            getNextHandler().processRequest(request);
        } else {
            throw new HandlerFaliureException("Invalid backup state!, valid backup status shoule be [" + BackupStatus.INITIALIZED + "] in this stage.");
        }
    }
}
මෙහිදී parent class එකෙහි ඇති processRequest(BackupRequest) නම් method එක override කිරීම සිදුකිරීම අනිවාර්යයෙන්ම කල යුතුය. අදාල කාර්යය code කරනුයේ මෙම method එක තුලයි. මෙහි model එකෙහි state එක validate කිරීමක්ද සිදුකර ඇති බව ඔබට දැකගත හැකිවනු ඇත. එය අනිවාර්ය නොවූවත් requirement එක මත අවස්ථානුකූලව එය යොදාගැනීම වටී. අදාල කාර්යය සිදුකිරීමෙන් අනතුරුව කරනුයේ ඊලඟ responsibility එක ආරම්භ කිරීමට විධාන කිරීමයි.
ZipProcessHandler.java
package responsibility;

import java.io.File;
import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class ZipProcessHandler extends DBBackupHandler {

    public ZipProcessHandler(DBBackupHandler handler) {
        setNextHandler(handler);
    }

    @Override
    void processRequest(BackupRequest request) throws HandlerFaliureException {
        if (request.getStatus() == BackupStatus.DUMPED) {
            System.out.println("Successfully ziped database : " + request.getDumpFile());
            request.setStatus(BackupStatus.ZIPED);
            request.setZipedDumpFile(new File("001.sql.zip"));
            getNextHandler().processRequest(request);
        } else {
            throw new HandlerFaliureException("Invalid backup state!, valid backup status shoule be [" + BackupStatus.DUMPED + "] in this stage.");
        }
    }
}
UploadToDropboxHandler.java
package responsibility;

import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class UploadToDropboxHandler extends DBBackupHandler {

    public UploadToDropboxHandler(DBBackupHandler handler) {
        setNextHandler(handler);
    }

    @Override
    void processRequest(BackupRequest request) throws HandlerFaliureException {
        if (request.getStatus() == BackupStatus.ZIPED) {
            System.out.println("Successfully uploaded backup file : " + request.getZipedDumpFile());
            request.setStatus(BackupStatus.UPLOADED);

            getNextHandler().processRequest(request);
        } else {
            throw new HandlerFaliureException("Invalid backup state!, valid backup status shoule be [" + BackupStatus.ZIPED + "] in this stage.");
        }
    }
}
CleanaupHandler.java
package responsibility;

import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class CleanUpHandler extends DBBackupHandler {

    @Override
    void processRequest(BackupRequest request) throws HandlerFaliureException {
        if (request.getStatus() == BackupStatus.UPLOADED) {
            System.out.println("Successfully clean up tmp files : " + request.getDumpFile() + "," + request.getZipedDumpFile());
            request.setStatus(BackupStatus.COMPLETED);
        } else {
            throw new HandlerFaliureException("Invalid backup state!, valid backup status shoule be [" + BackupStatus.UPLOADED + "] in this stage.");
        }
    }
}
මීලඟට සිදුකල යුත්තේ handlers එකට අමුනා responsibility  chain එක සකසා ගැනීමයි. එය නොයෙකුත් ආකාරයන්ගෙන් සිදුකල හැක. මෙමෙ උදාහරණයේදී එය සිදුකරන්නේ BackupProcessor නම් ක්ලාස් එක භාවිතයෙනි.
BackupProcessor.java
package responsibility;

import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class BackupProcessor {

    DBBackupHandler initialHandler;

    public BackupProcessor() {
        buildChainOfResponsibility();
    }

    private void buildChainOfResponsibility() {
        initialHandler =
                new DumpProcessHandler(
                new ZipProcessHandler(
                new UploadToDropboxHandler(
                new CleanUpHandler())));
    }

    public void startBackup(BackupRequest reqest) throws HandlerFaliureException {
        initialHandler.processRequest(reqest);
    }
}
අවසාන වශයෙන් අදාල backup process එක පාලනය කිරීම client class එක මගින් සිදුකෙරේ.
BackupClient.java
package responsibility;

import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class BackupClient {

    BackupProcessor processor = new BackupProcessor();

    public void startBackup(BackupRequest request) throws HandlerFaliureException {
        processor.startBackup(request);
    }
}
ChainOfResponsibilityDemo.java
package chainofresponsibilitydemo;

import responsibility.BackupClient;
import responsibility.BackupRequest;
import responsibility.BackupStatus;
import responsibility.exception.HandlerFaliureException;

/**
 *
 * @author kanishka
 */
public class ChainOfResponsibilityDemo {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws HandlerFaliureException {
        BackupClient client = new BackupClient();
        BackupRequest request = new BackupRequest();
        request.setStatus(BackupStatus.INITIALIZED);
        request.setDbName("paymentDB");
        client.startBackup(request);
    }
}
output :


Main method එක තුල සිදුකර ඇත්තේ අදාල request object එක සාදාගෙන එය chain of responsibility එකට ඇතුලු කිරීමයි. ඉන් අනතුරුව එය chain එක හරහා ගමන්කරනු ඇත. එහිදී අප විසින් අපේක්ෂා කරන කාර්යය සිදුවේ. යම් හෙයකින් chain එක අතරමැදදී අනපේක්ෂිත ලෙස ඉදිරියට යා නොහැකි අන්දමින් යමක් බිඳවැටුනහොත් එය client class වෙත දැනුම් දීම සඳහා අපට custom exceptions භාවිතා කල හැකිය. මෙම උදාහරණයේ එලෙස එක exception එකක් යොදාගෙන ඇත(HandlerFailureException). 


මෙම ලිපියට අදාල යම් ගැටලු හෝ අපහැදිලි තැන් ඇත්නම් comment ආකාරයෙන් හෝ ඉදිරිපත් කර ඒවා නිරාකරනය ගන්න. මෙහිදී යොදාගත් උදාහරණයට අදාල සියලුම source code පහත දක්වා ඇති git repository එකට පිවිසීමෙන් ලබාගත හැකිය.
මීට පෙර design pattern පිලිබඳව ඇති ලිපි වෙත පහත ලින්ක් තුලින් පිවිසිය හැකිය.

13 comments:

  1. Please meka continue karanna. good luck

    ReplyDelete
  2. niyamay , anneeeeee digatama lyyanna

    ReplyDelete
  3. සහෝ ඔබගෙ එක අති සාර්තකයි.හැමදේකටම ගොඩක් ස්තූතියි.වෙලාව තියන විදිහට දිගටම ලියමු.

    ReplyDelete
  4. Niyamay ayye.oyage lipi godak watinawa.java related frameworks lipiyakuth liyanna

    ReplyDelete
  5. digatama karagenayanna suba pathanawa

    ReplyDelete
  6. ස්තුතියි මල්ලි. ඉදිරියේදි ඒ සම්බන්දවත් ලියන්නම්.

    ReplyDelete
  7. elanga post aka kawadada daanneeeeeeeeeeeeee............

    ReplyDelete
  8. පිං සිද්ධ වෙනව බං. ICT වල හමබ වෙන්නෙම සල්ලි පෙරේතයො. ඒත් උබ දුප්පත් කොල්ලන්ට දෙයියෙක්... ජයෙන් ජය

    ReplyDelete
  9. උම්මා මචං........... අඩේ උබ වගේ උන් අපිට සම්පතක්. - අනේ අයියේ මට තව ප්‍රශ්න වගයකුත් තියෙනවා.. ම මේල් එකක් දාන්නම්කෝ... මමත් උසස්පෙළ තොරතුරැ තාක්ෂණය කරන ගමන්...............

    ReplyDelete
  10. ayye sirawatama..IT field eke bahutarayak inne salli peretayo.ayya wage aya me ratatama sampatak.digatama lessons demu.mata mewwa hugak wadagat..ayyata pin sidda wenawa me karana wadeta.parakku nokara digatama lessons denna time tiyana hatiyata..thank u very much!

    ReplyDelete
  11. හොඳයි මල්ලි වෙලාව තියන විදියට තවත් ලිපි ලියන්නම්.

    ReplyDelete
  12. ඇයි තාම පෝස්ට් එකක් නැත්තෙ අයියෙ? ගොඩ දවසකින් පෝස්ට් එකක් කලේ නෑනෙ.මේ පාඩම් ටික ගොඩක් වටිනව අපිට.දිගටම කරගෙන යමු අයියෙ.

    ReplyDelete
  13. bookmarked!!, I like your website!

    ReplyDelete