نظرة على static factory method

3zcsمنذ 6 سنوات

بسم الله الرحمن الرحيم

عندما نريد إنشاء  instance من class معين فإننا غالبا ما نقوم بإستخدام public constructor, لكن هنا طريقة أخرى, يجب أن يكون المبرمج ملماً بها, ألا وهي  public static factory method. ببساطة شديدة هي دالة static تقوم بإرجاع -return- لـ instance   من  class, على سبيل المثال:


public class Client {
    public static Client getInstance() {
        return new Client();
    }
}

كما نلاحظ أن المفهوم في غاية البساطة, لكن متى أحتاج أن أستخدمه ؟ , سنقوم الان بالمقارنة بينه وبين  Constructor, ونستعرض المميزات والعيوب.

أولا, تتميز static factory method بكونها قابلة لتسمية, فعلى سبيل المثال لو كان لدينا مركز رياضي ونوعين من العملاء, مشترك وزائر, المشترك ندخل أسمه, أما الزائر فنكتب فقط زائر, فسيكون شكل class كالتالي


public class Client {
    private String name;
    
    public static Client getVisitorInstance() {
        Client client = new Client();
        client.name = "visitor";
        return client;
    }

    public static Client getSubscribedInstance(String name) {
        Client client = new Client();
        client.name = name;
        return client;
    }
}

دعنا نقوم بإستدعائها, واستدعاء constructor.


    Client client1 = Client.getVisitorInstance();
    Client client2 = Client.getSubscribedInstance("Abdulaziz");
    
    Client client3 = new Client();
    Client client4 = new Client("Abdulziz");

كما نلاحظ, في static factory method من النظرة الأولى, تمكنت من معرفة نوع العميل, بالإضافة إلى أنه لو أراد شخص ما إكمال تطوير المشروع, سيتمكن من التعامل مع class الذي قمت ببنائه دون النظر فيه -Readability-, على عكس  public constructor.

ثانيا, من خلال static factory method يمكنك أن تجبر مستخدم الـ class على إنشاء instance واحد فقطلهذا الـ class, وهو ما نسميه بـ singleton, كما في المثال


public class Client {
    private static final  Client client = new Client();
    
    private Client(){
        
    }

    public static Client getClient() {
        return client;
    }
}

كما نلاحظ أن constructor private, بمعنى أنه لا يمكنك إنشاء instance عن طريقه, لكن تستخدم static factory method والتي دائما ستعيد لك نفس  object الذي هو client, وبهذا لن تحصل إلا على object واحد فقط عن طريق هذا class.

ثالثا, يمكن لـ static factory method أن يقوم بعمل return لـ  instance من sub-type لـ class, على سبيل المثال لدينا class يقوم بإزالة الزوائد من القيم الداخلة إليه, مثلا لو تلقى قيمة 0010 يقوم بحذف الأصفار, ولو تلقى “محمد  ” يقوم بحذف space وهكذا, لنبدأ في الكود.
بداية سنقوم بإنشاء Interface بإسم  trimmer


public interface Trimmer<T> {
    T trim();
}

الان, سنقوم بإنشاء class لتعامل مع String


public class StringTrim implements Trimmer<String> {
    String value;

    StringTrim(String value) {
        this.value = value;
    }

    @Override
    public String trim() {...}
}

وأخر لتعامل مع Integer


public class IntegerTrim implements Trimmer<Integer> {
    Integer value ;

    IntegerTrim(Integer value) {
        this.value = value;
    }

    @Override
    public Integer trim() {...}
}

نلاحظ أن access modifier  لـ constructor من نوع  package-private أي لا يمكن الوصول إليه من خارج  package,  ثم نقوم بإنشاء class  نستخدمه إذا ما أردنا الإستفادة من classes السابقة


public class Trimmers {
    private Trimmers(){}

    public static Trimmer<String> getTrimString(String s){
        return new StringTrim(s);
    }

    public static Trimmer<Integer> getTrimInteger(Integer i){
        return new IntegerTrim(i);
    }

}

نلاحظ هنا كيف استفدنا من static factory method, بحث أنه قام بإرجاع object, عبارة عن sub-type لـ return type.

وفي main نستدعيه بالطريقة التالية:


        Integer i = Trimmers.getTrimInteger(0150).trim();
        String s = Trimmers.getTrimString("  abdulaziz").trim();

وهذا الأسلوب يسمى  interface-based API أو interface-based frameworks

بعد استعراضات المميزات لـ static factory method, لنستعرض السلبيات, وهما اثنتين.

الأولى, لا يمكن الوراثة -inheritance- من class لا يوجد فيه public or protected constructor, ففي هذه الحالة لا يمكن لـ static factory method أن تعوض constructor.

الثانية, أن static factory method قد لا يمكن تمييزها بسهولة عن بقية static methods التي بداخل class,  لذلك تستخدم عادة بعض التسميات المتعارف عليها, لتحل هذه المشكلة.

الان, لنمثل علىstatic factory method في Android, فلو أردنا أن نقوم بإنشاء activity داخلها fragment سيكون الكود كالتالي


public class MainActivity extends AppCompatActivity {

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        FragmentManager manager = getSupportFragmentManager();
        Fragment fragment = manager.findFragmentById(R.id.fragment);
        if (fragment == null){
            fragment = new BlankFragment();
            manager.beginTransaction().add(R.id.fragment,fragment).commit();
        }

    }

}

وفي Activity أخرى


public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_secound);
        FragmentManager manager = getSupportFragmentManager();
        Fragment fragment = manager.findFragmentById(R.id.fragment);
        if (fragment == null){
            fragment = new BlankFragment();
            manager.beginTransaction().add(R.id.fragment,fragment).commit();
        }

    }
    
}

لنقوم الان بتحسين هذا الكود, سنقوم بالتالي, أولا بناء static factory method داخل fragment


    public static BlankFragment newInstance() {
        BlankFragment fragment = new BlankFragment();

        return fragment;
    }

ثم نقوم ببناء BaseActivity كالتالي


public abstract class BaseActivity extends AppCompatActivity {
    
    protected abstract Fragment createFragment();
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        FragmentManager manager = getSupportFragmentManager();
        Fragment fragment = manager.findFragmentById(R.id.fragment);
        if (fragment == null){
            fragment = createFragment();
            manager.beginTransaction()
                    .add(R.id.fragment,fragment)
                    .commit();
        }
    }

}

لاحظ هنا أن هناك دالة ستعيد لنا fragment سنستخدمها داخل  oncreate, وستظهر فائدة هذا الشيء عندما نجعل MainActivityb و SecoundActivity ترث منها, فيكون شكل الكود  كالتالي


public class SecondActivity extends BaseActivity {

    @Override
    protected Fragment createFragment() {
        return BlankFragment.newInstance();
    }
}

public class MainActivity extends BaseActivity {
    
    @Override
    protected Fragment createFragment() {
        return BlankFragment.newInstance();
    }
}

إلى هنا أتمنى أن أكون وفقت في تبسيط هذا المفهوم,

والسلام عليكم ورحمة الله وبركاته.المصادر:
Big nerd ranch Android
Effective java

Static factory methods vs traditional constructors

كلمات دليلية:
0
إعجاب
4564
مشاهدات
0
مشاركة
0
متابع
متميز
محتوى رهيب

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !