面向对象设计六大原则:
开闭原则
依赖倒置
里氏替换原则
单一职责
接口隔离
迪米特法则
结构原则:
面向接口编程,原则上对象之间的关系尽量采用组合的方式而不是继承的方式,集成尽量不重写超类的方法。
工厂方法有三种:简单工厂方法,静态工厂方法,抽象工厂方法。
工厂方法模式为创建对象的模式,为了解决的问题是对象创建的问题。
1.简单工厂方法:
类图
代码实现如下:
interface Product {
void print();
}
class P1 implements Product {
@Override
public void print() {
System.out.println("生产出产品p1");
}
}
class P2 implements Product {
@Override
public void print() {
System.out.println("生产出产品p2");
}
}
//简单工厂方法
class SimpleFactory {
public Product getProduct(String productName) {
if(productName.equals("p1"))
return new P1();
else if(productName.endsWith("p2"))
return new P2();
else
return null;
}
}
客户端调用:
// 简单工厂模式
SimpleFactory simpleFactory = new SimpleFactory();
simpleFactory.getProduct("p1").print();
simpleFactory.getProduct("p2").print();
结果:
生产出产品p1
生产出产品p2
分析:简单工厂在产品的种类不多的情况下,可以解决问题,但是增加产品要去修改工厂类
这里我们提出三个问题:
1、字符串这样去选择控制对象生成很不方便,容易出错,而且一个函数的功能太臃肿不利于维护。
2、每次需要工厂都需要实例化,浪费内存。
3、如果增加产品,会修改原来的类,不符合开闭原则。
接下来我们进一步优化这个架构,增加它的健壮性。
第一个问题的解决方案
拆分成不同的方法,直接调用方法,生成不同的产品,不用传入参数
class SimpleFactory2 {
public Product getP1() {
return new P1();
}
public Product getP2() {
return new P2();
}
}
客户端调用:
// 字符串容易出错,拆分函数改进
System.out.println("----拆封方法,消除字符串----");
SimpleFactory2 simpleFactory2 = new SimpleFactory2();
simpleFactory2.getP1().print();
simpleFactory2.getP2().print();
运行结果:
----拆封方法,消除字符串----
生产出产品p1
生产出产品p2
这样直接调用方法,不用传入参数,分离了接口方法,解决了问题。
然后我们来解决第二个问题。我们创建对象为了节省内存,应该采用静态方法的模式去解决。不用每次都新生成对象吧。
// 第二个问题的解决方案
/*
* 太过于浪费内存,可以采用静态方法的模式去生成
*/
class SimpleFactory3 {
public static P1 p1;
public static P2 p2;
public static Product getP1() {
if(p1 == null)
p1 = new P1();
return p1;
}
public static Product getP2() {
if(p2 == null)
p2 = new P2();
return p2;
}
}
运行结果:
---修改静态方法----
生产出产品p1
生产出产品p2
第三个问题就是如果要增加新的具体的产品p3,那就要修改工厂类了,按现在的情况,必须要在类中添加新的方法。这样:
class SimpleFactory3 {
public static P1 p1;
public static P2 p2;
public static P3 p3;
public static Product getP1() {
if(p1 == null)
p1 = new P1();
return p1;
}
public static Product getP2() {
if(p2 == null)
p2 = new P2();
return p2;
}
public static Product getP3() {
if(p3 == null)
p3 = new P3();
return p3;
}
想象如果具体的产品非常多,难道每次都要去修改这个工厂么。这样明显是不符合开闭原则的。好吧。工厂的目的就是生产出产品,我们应该把稳定不变的部分抽象出来,这里工厂的抽象功能就是创造产品。我们定义一个抽象类。
abstract class AbsctractFactory {
public abstract Product getProduct();
}
我们具体要生产什么产品,由具体的工厂来实现,例如生产产品P1的工厂修改为:
class P1SimpleFactory extends AbsctractFactory {
@Override
public Product getProduct() {
return new P1();
}
}
同理生产产品P2修改为:
class P2SimpleFactory extends AbsctractFactory {
@Override
public Product getProduct() {
return new P2();
}
}
客户端运行如下
// 添加产品,不用修改原来的类了
System.out.println("--抽象出工厂--");
AbsctractFactory absctractFactoryP1 = new P1SimpleFactory();
absctractFactoryP1.getProduct().print();
AbsctractFactory absctractFactoryP2 = new P2SimpleFactory();
absctractFactoryP2.getProduct().print();
这个时候我们如果需要产品P3:
// 添加新的具体产品,不需要修改现成的代码,只需定义一个新的产品P3实现原来的产品接口,再添加一个具体工厂即可
class P3 implements Product {
@Override
public void print() {
System.out.println("生产出新的产品p3");
}
}
增加新产品工厂代码
class P3SimpleFactory extends AbsctractFactory {
@Override
public Product getProduct() {
return new P3();
}
}
客户端调用:
// 添加新的产品
AbsctractFactory absctractFactoryP3 = new P3SimpleFactory();
absctractFactoryP3.getProduct().print();
运行结果:
生产出新的产品p3
我们已经将工厂进行了抽象,现在的结构如下
这样的好处就是增加新的产品可以不用再修改原来的工厂了,满足了开闭原则。
————-分割线————-
到此我们完成了单一产品接口的创建,但我们如果是后期需要抽象出更多的产品接口,那我们这个系统就不是那么好了。我们可以重新定义抽象工厂的方法。
我们来举个例子,那就是生产CPU和GPU,GPU是图形显卡的核心,AMD和Intel可以设计CPU,AMD,NVIDIA公司设计GPU。一般制造工作交给代工厂TSMC或则是SAMSUNG,多个产品类别,那我们就在抽象工厂方法里添加产品的方法:
两个产品类别(接口):
// 如果要添加新的,添加新的产品类别
interface CPU {
void print();
}
interface GPU {
void print();
}
class AMD implements CPU,GPU {
@Override
public void print() {
System.out.println("我是AMD,设计出了CPU,GPU");
}
}
class Intel implements CPU {
@Override
public void print() {
System.out.println("我是intel,设计出CPU");
}
}
class NVIDIA implements GPU {
@Override
public void print() {
System.out.println("我是nVIdia,设计出GPU");
}
}
// 抽象工厂具有两个函数生产产品
abstract class OEMFactory {
public abstract CPU produceCPU();
public abstract GPU produceGPU();
}
具体的工厂类:
/class TSMC extends OEMFactory {
static {
System.out.println("TSMC生产...");
}
@Override
public CPU produceCPU() {
return new AMD();
}
@Override
public GPU produceGPU() {
return new NVIDIA();
}
}
class SAMSUNG extends OEMFactory {
static {
System.out.println("SAMSUNG生产...");
}
// 不得不实现该方法 这里怎么解决呢?
@Override
public CPU produceCPU() {
return null;
}
@Override
public GPU produceGPU() {
return new AMD();
}
}
客户端调用:
// 添加新的产品类别
System.out.println("---添加新的产品类别----");
OEMFactory omeFactory1 = new TSMC();
omeFactory1.produceCPU().print();
omeFactory1.produceGPU().print();
OEMFactory omeFactory2 = new SAMSUNG();
// 三星不制造CPU omeFactory2.produceCPU().print();
omeFactory2.produceGPU().print();
就如上面谈及到的,根据接口隔离原则,我们可以实现多个接口的方式去解决继承多余的方法的问题。而且如果有新的产品线,我们只需要定义新的接口(抽象工厂)就可以了,不用去修改原来的接口。
把抽象类拆分成接口之后的效果:
// 这样我们如果要添加新的产品类别(接口),需要修改抽象工厂类,不符合开闭原则
// 我们需要将抽象工厂拆分成多个部分,设计原则里面有个接口隔离原则,将抽象类进行拆分成接口
interface CPUOEMFactory {
CPU produceCPU();
}
interface GPUOEMFactory {
GPU produceGPU();
}
class TSMC2 implements CPUOEMFactory,GPUOEMFactory {
static {
System.out.println("TSMC生产...");
}
@Override
public GPU produceGPU() {
return new NVIDIA();
}
@Override
public CPU produceCPU() {
return new AMD();
}
}
class SAMSUNG2 implements GPUOEMFactory {
static {
System.out.println("SAMSUNG第二个工厂,生产...");
}
@Override
public GPU produceGPU() {
return new NVIDIA();
}
}
客户端调用:
// 拆分了接口
System.out.println("----拆分了接口---");
CPUOEMFactory cpuOEMFactory = new TSMC2();
cpuOEMFactory.produceCPU().print();
// TSMC同时实现了GPU制造
GPUOEMFactory gpuOEMFactory = new TSMC2();
gpuOEMFactory.produceGPU().print();
GPUOEMFactory gpuOEMFactory2 = new SAMSUNG2();
gpuOEMFactory2.produceGPU().print();
结果:
----拆分了接口---
TSMC生产...
我是AMD,设计出了CPU,GPU
我是nVIdia,设计出GPU
SAMSUNG第二个工厂,生产...
我是nVIdia,设计出GPU
我们只需实现我们需要的抽象类(接口)即可。这样我们保证了多个产品,多个工厂的方式搭建起一个稳定的架构,多扩展开放,对修改关闭。
如果新增一个产品线:
// 只需要增加抽象工厂类接口,不需要修改现有代码
interface ARMOEMFactory {
ARM produce();
}
// 具体制造工厂
class TSMC3 implements ARMOEMFactory {
static {
System.out.println("TSMC 第三个工厂,制造了...");
}
// TSMC既制造高通,也制造苹果ARM,构造方法自己指定
ARM arm;
TSMC3(ARM arm) {
this.arm = arm;
}
@Override
public ARM produce() {
return arm;
}
}
class SAMSUNG3 implements ARMOEMFactory {
static {
System.out.println("SAMSUNG第三个工厂,制造了...");
}
@Override
public ARM produce() {
return new Qualcomm();
}
}
客户端调用:
// 增加了新的产品类别(接口)ARM
System.out.println("-----增了新的产品类别-----");
ARMOEMFactory armFactory = new TSMC3(new Qualcomm());
armFactory.produce().printARM();
ARMOEMFactory armFactory2 = new SAMSUNG3();
armFactory2.produce().printARM();
结果:
-----增了新的产品类别-----
TSMC 第三个工厂,制造了...
我是高通,设计了820 ARM处理器
SAMSUNG第三个工厂,制造了...
我是高通,设计了820 ARM处理器