Simple Factory,Factory method & abstract Factory design pattern

Tue, Mar 1, 2022 6-minute read

Small project to learn this design pattern

1. simple factory: design a calculator, hava basic operator(+-*/)

check my final code

image

simple factory

my old code as newbie πŸ˜… πŸ˜… πŸ˜…

public interface Operation {

   public double numberA;
   public double numberB ;

   public double getRes();

Interfaces don’t contain fields because fields represent a specific implementation of data representation, and exposing them would break encapsulation. Thus having an interface with a field would effectively be coding to an implementation instead of an interface

public interface Operation {
    public double getRes(double numberA, double numberB);
}

more to referinterface

v1.1: add a new operation “sqrt”

so basicly, you are not just adding sqrt in the OperationFactory,

but return to a new factory: OperationAdd();

if you want to add more operator, would be:

sqrt –> sqrtFactory, xxx –>xxxFactory

every factory would be managed in OperationFactory

static

public class OperationFactory {

    public  static Operation createOper(String operate) { 

2. factory method: design a calculator, hava basic operator(+-*/)

check my final code

In simple factory, when you need to add more operator, you would modify the code in OperationFactory, which break the Open Close principle

This Operator Factory is tightly coupled with its branch, so we make this Operator Factory abstract.

public interface IFactory {
    public Operation createOper();
}
class AddFactory implements IFactory {


    @Override
    public Operation createOper() {
        return new OperationAdd();
    }
}

class MinFactory implements IFactory {


    @Override
    public Operation createOper() {
        return new OperationMin();
    }
}

IFactory => Operation

  • AddFactory return OperationAdd =>@Override getRes() of Operation
  • MinFactoryreturn OperationMin=>@Override getRes() of Operation
  • ,

as Client:

  1. decide which factory to use:

IFactory factory = new AddFactory();

Operation op = factory.createOper(); double res = op.getRes(numberA, numberB); System.out.println(res);

so in factory method, if you want to add more function, you need to modify in client view, rather than in factory class

image

simple factory

woo, every time we need a new operator, we also need a related factory.

3. (factory method) design a “Customer loyalty program”, include : coupon, goods, gift card

3 interface:

  1. Counpon: CouponResult sendCoupon(String uId, String couponNumbner, String uuid);

  2. Goods: Boolean deleverGoods (DeliverReq req);

  3. gift card: void grantToken(String phoneNumber, String cardId);

my code


@Slf4j

use Slf4j in lombok, you don’t need below code.

private final Logger logger = LoggerFactory.getLogger(XXX.class)

getting to know more about @slf4j,

click here Jackson vs Lombok

log.info(“xxxx{}”, key);


IReward interface

  • CardImpl => CardService => void grantToken()
  • CouponImpl => CouponResult => sendCoupon()
  • GoodImpl, =>GoodService =>Boolean deliverGoods()

as Client:

  1. decide which factory to use:

IReward cardSer = new CardImpl();

cardSer.sendReward(“1003”, “AMZ102345EOIJIOJO IOJHOI”, null, null);

 @Test
    public void test_commodity3() throws Exception {
        IReward cardSer = new CardImpl();

        cardSer.sendReward("1003", "AMZ102345EOIJIOJO IOJHOI", null, null);


    }

or you can put all the logic in client class

public class StoreFactory {

    public IReward getRewardService(Integer rewardType) {
        switch (rewardType) {
            case 1:
                return new CouponImpl();

            case 2:
                return new GoodImpl();
            case 3:
                return new CardImpl();
            default:
                throw new RuntimeException("there is no such service");
        }
    }

    @Test
    public void test_commodity() throws Exception {
        StoreFactory storeFactory = new StoreFactory();

        IReward coupService = storeFactory.getRewardService(1);

        coupService.sendReward("1001", "EGM102345", "3749898", null);

    }


4. design a basic DataBase program

You offer the client two options: sqlServer, AccessService. Both realize the function “insert”, “GetUser”. the User class, you only need two fields: ID, Name "

project structure:

IFactory =>IUser

  • SqlFactory => IUser Create() => SqlServiceUser
  • AccessFactory=> IUser Create() => AccessServiceUser

as Client:

  1. decide which factory to use:

IFactory factory = new SqlServerFactory();

IUser iu = factory.CreateUser();

This is the decouple of Moder layer and Controller layer

5. add a department form to the basic DataBase program

lots of work to do πŸ˜… πŸ˜… πŸ˜…

IFactory =>IUser + IDepartment

  • SqlFactory => IUser Create() + IDepartment Create() => SqlServiceUser + SqlServiceDep
  • AccessFactory=> IUser Create() + IDepartment Create() => AccessServiceUser + AccessServiceDep

The difference between factory method and abstract factory is IFactory contains more than just one product.


review our first simple factory calculator: return the OperationImpl, not the factoryImpl

OperationFactory => static Operation createOper

  • return OperationAdd() => @override getRes from Operation

  • return OperationMin()


Improve our code a little bit:

IFactory program

IFactory => (2 methods)

  • IUser CreateUser()
    • return “SqlserveUser()" => @override insert from “IUser”
    • return “AccessServiceUser()"=> @override insert from “IUser”
  • IDepartment CreateDept()
    • return “SqlserveDepart()" => @override insert from IDepartment
    • return “AccessServiceDepart()" => @override insert from IDepartment

Client view:

User user = new User(); Department department = new Department(); IUser iUser = IFactory.CreateUser(); iUser.insert(user);

There is no SQL server or Access in Client view.

6. interface and abstract factory in abstract factory

public class IFactory {

    private static final String db = "SqlServer";
    private static String className = null;

       public static IUser CreateUser() {
       IUser result = null;
       switch(db) {

all fields in Interfaces is public static final type, cannot assign a value to final variable

7. now our client want to use Oracle database

When our code is in this structure:

IFactory => (2 methods)

  • IUser CreateUser()
    • return “SqlserveUser()" => @override insert from “IUser”
    • return “AccessServiceUser()"=> @override insert from “IUser”
  • IDepartment CreateDept()
    • return “SqlserveDepart()" => @override insert from IDepartment
    • return “AccessServiceDepart()" => @override insert from IDepartment

We just add more abtract factory: OracleFactory.

But since we improve our code, we need modify the code in IFactory switch branch πŸ˜… πŸ˜…πŸ˜…

private static final String db = “SqlServer”;

If we change db = “OracleSerive”,

and the later execution according to the db name?

reflection can do that:

Class.forName(className).newInstance()

Using class
public class IFactory {

    private static final String db = "SqlServer";
    private static String className = null;

            static IUser CreateUser() {
                className = db + "User";

                try {
                    return (IUser) Class.forName(className).newInstance();
                    //SqlserveUser()
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return null;

            }

            static IDepartment CreateDept() {
                className = db + "Department";

                try {
                    return (IDepartment) Class.forName(className).newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return null;

            }
}

Now we just add Oracle related classes, and modify the code:

public class IFactory {

    private static final String db = "OracleServer";

Now we need project class?

add 3 Project related class,(IProject, AccessServiceProj, SqlProj), modify in the IFacotry.

static IProject CreatePro() {\

8. Using properties to fix the problem that you need modify in the IFactory

public class IFactory {

    private static final String DB = null;
    private static String className = null;
    private static Properties properties = new Properties();

            static {
                try {
                    propperties.load(DataAccess.class.getClassLoader().getResourceAsStream("config/app.properties"));
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
               DB =  properties.getProperty("DB");
            }

            static IUser CreateUser() {
                return (IUser) create("User");
            }

            static IDepartment CreateDept() {
                return (IDepartment) create("Department");
            }

            public static Object create(String name) {
                className = DB + name;
            
                try {
                    return Class.forName(className).newInstance();
                    //SqlserveUser()
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return null;

            }

}

9. upgrade your company redis system to A or B cluter

start code#39-42

  • Bcluter
  • Acluter
  • RedusUtils

IFactory => (2 methods)

  • IUser CreateUser()
    • return “SqlserveUser()" => @override insert from “IUser”
    • return “AccessServiceUser()"=> @override insert from “IUser”
  • IDepartment CreateDept()
    • return “SqlserveDepart()" => @override insert from IDepartment
    • return “AccessServiceDepart()" => @override insert from IDepartment

CacheService

  • AclusterServiceImpl =>( Acluster acluster) => @override get(), set() from “CacheService”, use (acluster.gain(key)…)

  • BclusterServiceImpl=>( Bcluster bacluster) => @override get(), set() from “CacheService”, use (bcluster.get(key)…)

interface use JDKProxy getProxy


Ok, It’s time for dp2