النموذج Factory

وصف النموذج

ليكن لدينا الصف الأب Super ومجموعة الصفوف الأبناء Sub1، Sub2، Sub3 يعرف النموذج Factory بأنه النموذج الإنشائي الذي يمكننا من الحصول على كائن من أحد الصفوف الأبناء Sub1، Sub2، Sub3 بشكل آلي دون أن نقوم بعملية بناء الكائن بشكل يدوي، فقط نقوم بتحديد أي من الأبناء نرغب بالحصول على كائن منه حيث يتكفل النموذج بعملية البناء بالشكل المناسب.

factory-01

متى نستخدم النموذج Factory

عندما يوجد صف عام (أب) يرثه مجموعة من الصفوف المخصصة (أبناء) ونرغب أثناء تنفيذ البرنامج بالحصول على كائنات من الصفوف الأبناء دون القيام بعملية البناء بشكل يدوي في كل مرة.

كما في الشكل السابق نلاحظ أن الصف Client يرغب بالحصول على كائنات من نمط Product (صف أب) لكن في كل مرة يحتاج لنوع مختلف من الأبناء فمرة قد يحتاج للحصول على Vanilla ومرة أخرى يحتاج للحصول على Chocolate … فبدل أن يقوم الصف Client بعملية الإنشاء بشكل يدوي يطلب الكائنات من الصف Factory ومع كل طلب يقوم بتحديد نوع المنتج المرغوب.

كيفية تطبيق النموذج Factory

ليكن لدينا الصف Super ومجموعة الأبناء Sub1,2,3

/**
 *
 * @author Mutasem
 */
public class Super {
    
}
class Sub1 extends Super{
    
}
class Sub2 extends Super{
    
}
class Sub3 extends Super{
    
}
=

لتطبيق النموذج نقوم بتعريف الصف SuperFactory الذي يملك دالة ولتكن getSuper معرفة بالشكل التالي:

/**
 *
 * @author Mutasem
 */
public class SuperFactory {
    public static Super getSuper(String type){
    if(type.equalsIgnoreCase("Sub1"))
        return new Sub1();
    if(type.equalsIgnoreCase("Sub2"))
        return new Sub2();
    if(type.equalsIgnoreCase("Sub3"))
        return new Sub3();
    return null;
    }
}

ملاحظة

هذه الطريقة العامة للحل يمكنك كتابة شيفرة برمجية تتناسب أكثر مع متطلبات المشروع الخاص بك.

مثال للتوضيح

ليكن لدينا الصف العام Connection ومجموعة الصفوف الأبناء SqlServerConnection وMySQLConnection وOracleConnection.

/**
 *
 * @author Mutasem
 */
public abstract class Connection {
    public abstract void connect();
}
class SqlServerConnection extends Connection {
    public Object ServerDeriver, ServerConfig1, ServerConfig2;
    @Override
    public void connect() {
//Sql server connection logic
    }
}
class MySQLConnection extends Connection {
    public Object MySQLDriver,MySQLConfig;

    @Override
    public void connect() {
//MySQL connection logic
    }
}
class OracleConnection extends Connection {
    public Object OracleDriver,OracleConfig1, OracleConfig2, OracleConfig3;
    @Override
    public void connect() {
//Oracle connection logic
    }
}

ليكن لدينا الصف Client الذي يرغب بالاتصال بقاعدة بيانات MySQL فيكون الحل بدون استخدام النموذج Factory بالطريقة التالية:

/**
 *
 * @author Mutasem
 */
public class Client {

    public static void main(String[] args) {
        /* A lot of code here */

        MySQLConnection connection = new MySQLConnection();
        connection.MySQLDriver = "MySQL Driver object";
        connection.MySQLConfig = "Specific Config";
        connection.connect();
        /* ...... */
    }
}


لنفرض أنه بسبب معين اضطررنا لتغيير مخدم قواعد البيانات من MySQL إلى Oracle بالتالي أصبح علينا تعديل الكود الخاص بالصف Client ليصبح بالشكل التالي:

/**
 *
 * @author Mutasem
 */
public class Client {

    public static void main(String[] args) {
        /* A lot of code here */
         
//        MySQLConnection connection=new MySQLConnection();
//        connection.MySQLDriver="MySQL Driver object";
//        connection.MySQLConfig="Specific Config";
        OracleConnection connection = new OracleConnection();
        connection.OracleDriver = "Oracle Driver";
        connection.OracleConfig1 = "Some Value";
        connection.OracleConfig2 = "Some Value";
        connection.OracleConfig3 = "Some Value";
        connection.connect();
        /* ...... */
    }
}


لاحظ حجم التعديل المطلوب على مستوى مشروع صغير وبشيفرة وهمية، قد يكون حجم التعديل أكبر بكثير في المشاريع الحقيقية.

الحل باستخدام النموذج Factory

/**
 *
 * @author Mutasem
 */
public class ConnectionFactory {
    public static Connection getConnection(String type) throws Exception {
        if (type.equalsIgnoreCase("MYSQL")) {
            MySQLConnection connection = new MySQLConnection();
            connection.MySQLDriver = "MySQL Driver object";
            connection.MySQLConfig = "Specific Config";
            return connection;
        }
        if (type.equalsIgnoreCase("SQLSERVER")) {
            SqlServerConnection connection = new SqlServerConnection();
            connection.ServerDeriver = "Driver object";
            connection.ServerConfig1 = "Specific Config1";
            connection.ServerConfig2 = "Specific Config2";
            return connection;
        }
        if (type.equalsIgnoreCase("ORACLE")) {
            OracleConnection connection = new OracleConnection();
            connection.OracleDriver = "Driver object";
            connection.OracleConfig1 = "Specific Config";
            connection.OracleConfig2 = "Specific Config2";
            connection.OracleConfig3 = "Specific Config2";
            return connection;
        }
        throw new Exception("Unsupported server");
    }
}

يصبح الصف Client كالتالي:



/**
 *
 * @author Mutasem
 */
public class FactoryClient {

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionFactory.getConnection("MYSQL");
        //or Connection connection=ConnectionFactory.getConnection("SQLSEVER");
        // or Connection connection=ConnectionFactory.getConnection("ORACLE");
        connection.connect();
    }
}

ملاحظة حول المثال

الكثير من المكتبات المستخدمة في التعامل مع قواعد المعطيات تستخدم النموذج Factory بشكل ضمني ولست مضطراً لإعادة كتابة هذا النموذج كما في المثال فهو للتوضيح فقط.

رابط السلسلة على GitHub

https://github.com/mutasemhajhasan/Design-Patterns

المراجع

Gamma, Erich ; Helm, Richard ; Johnson , Ralph ; Vlissides , John;. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Hawthorne, New York: Addison-Wesley.