简单工厂
引导
面向对象的好处:
通过封装、继承、多态把程序的耦合度降低。
就像是:传统印刷术的问题就在于所有的字都刻在同一版面上造成耦合度太高,活字印刷术就更加灵活。
开始用设计模式使得程序更加灵活,易于修改,并且易于复用。
当所有方法集中在一个类里,比如:
public class Opeartion {
/*
* 1 +, 2 -
*/
public static double getResult(double numA, double numB, int oper){
double result = 0;
switch (oper) {
case "+":// +
result = numA + numB;
break;
case "-":// -
result = numA - numB;
break;
default:
break;
}
return result;
}
}
public class Main {
public static void main(String[] args) {
double numA = 12;
double numB = 23;
double result = Opeartion.getResult(numA, numB, "+");
System. out.println("The result is :" + result);
}
}
当我想要增加功能时(比如加一个 " * " 功能),我需要回到代码里进行修改。看起来只是在Switch后面再加一个case,但是前面的其他功能还要重新编译。
业务的封装:
应该让业务逻辑和界面逻辑分开,让他们之间的耦合度降低,也就是应该把加减乘除等运算分离,修改其中一个不影响另外的几个,增加运算算法也不影响其他代码。
简单工厂模式 对修改封闭,对扩展开放
到底实例化谁,将来是不是增加实例化的对象,这是很容易变化的地方。应该考虑让一个单独的类来做这个创造实例的过程,这就是工厂。
例:计算
【Api】写一个抽象类定义需要的变量num1和num2并拿到结果方法getResult()
public abstract class Cal {
protected double num1;
protected double num2;
public double getNum1() {
return num1;
}
public double getNum2() {
return num2;
}
public void setNum1(double num1) {
this.num1 = num1;
}
public void setNum2(double num2) {
this.num2 = num2;
}
public abstract double getResult();
}
【Impl】一个具体的实现方法,这里只写一个“+”
public class OperationAdd extends Cal{
@Override
public double getResult() {
return num1 + num2;
}
}
【Factory】一个工厂类用于创建对象并选择到底实例化那个子类
public class CalFactory {
public static Cal createCal(String calType){//定义的
Cal cal = null;
switch (calType){//这里可以多写几个case,此处仅展示“+”
case "+":
cal = new OperationAdd();
break;
}
return cal;
}
}
【Clinit】具体操作输入
public class MainFactory {
public static void main(String[] args) {
Cal cal;
cal = CalFactory.createCal("+");
cal.num1 = 23;
cal.num2 = 10;
double result = cal.getResult();
System.out.println("result is " + result);
}
}
进一步思考
客户端直接使用的是接口,客户端不会和实现直接发生联系,客户端就被接口和实现隔离开了
接口是系统具有可插拔的功能,如果红色的部分是一个封装体的话,外面是不知道它的具体实现的,如果新加一种实现来实现这个API,客户端是不知道的。
这就相当于插入新的功能。过了一段时间ImplA已经没有用了,过时了,可以拔走,对客户端还是没有影响。
但是,还有问题就是:你会发现在客户端调用的时候,客户端不但知道了接口,同时还知道了具体的实现就是Impl,就像刚刚的实例里面在MainFactory我们输入的时候还是要输入“+”。而接口的思想是“封装隔离”,而Impl这个实现类,应该是被接口Api封装并同客户端隔离开的,也就是说,客户端根本就不应该知道具体的实现类是Impl。
而是用简单工厂模式客户端就和具体的实现ImplA,ImplB没有关系了,真正实现了隔离,体现了API的优势。
Factory和Api是暴露在封装体外供Client是用的,而ImplA和ImplB是在封装体内部的,Client是看不见的。
而Factory是在封装体的内部,自然知道有ImplA、ImplB,自然就可以直接new ImplA()、new ImplB(),
我封装的内部知道不需要告诉外部的Client,工厂只需要返回一个借口类型给客户端就可以了,到底返回的
是ImplA还是ImplB客户端是不知道的。
所以虽然只是new ImplA()、new ImplB()换了一个位置,但是确实实现了截然不同的功能。封装到Factory真正
实现Client和具体的实现的隔离。
简单工厂的核心是选择而不是实现
简单工厂的改进
利用反射加载配置文件中设置的数据库
普通代码用原始简单工厂改进后(原普通代码见反射)
知识点—反射
public abstract class AbstractDB {
public abstract void getConnection();
}
public class MySql extends AbstractDB{
@Override
public void getConnection() {
System.out.println("MySql.getConnection");
}
}
public class DBFactory {
public static AbstractDB getDB(String dbName){
AbstractDB db = null;
switch (dbName){
case "MySql":
db = new MySql();
break;
case "Oracle":
db = new Oracle();
break;
}
return db;
}
}
public class MainDB {
public static void main(String[] args) {
AbstractDB db = DBFactory.getDB("MySql");
}
}
简单工厂终极改进:反射+配置文件
public class DBTest {
public static void main(String[] args) throws Exception {
// AbstractDB db = new MySql();
// db.getConnection();
InputStream inputStream = new FileInputStream("D:\\development\\idea\\JavaSE\\situ[August]\\src\\reflect\\db.properties");//这里的地址得完整
Properties properties = new Properties();
properties.load(inputStream);
String className = properties.getProperty("className");
System.out.println(className);
//com.situ.day22.db.SqlServer
Class aClass = Class.forName(className);
AbstractDB db = (AbstractDB) aClass.newInstance();
db.getConnection();
}
}
在这个db.properties里输入指令就可以控制
className = reflect.MySql
虽然代码多了很多还看着很别扭但是真正实现了隔离。