单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
一、单例模式
作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
五种写法:饿汉模式、懒汉模式、双重检测锁、静态内部类实现延迟加载、枚举类实现单例(不细讲了,具体参考我的另一篇文章https://www.jianshu.com/p/e86833bee429)
二、简单工厂模式
2.1、作用:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
2.2、缺点:对于增加新产品无能为力!不修改代码的话,是无法扩展的,不完全满足开闭原则(如下面的例子,如果再增加华为电脑,则必须修改工厂类的代码)
2.3、UML图:
2.4、示例:
//电脑接口
public interface Computer {
public void play();
}
//联想电脑类
class LenovoComputer implements Computer{
public void play() {
System.out.println("我是联想电脑");
}
}
//苹果电脑类
class MacComputer implements Computer{
public void play() {
System.out.println("我是苹果电脑");
}
}
--------------------------------------------------------------------------------
//简单工厂类,类方法一般为静态的,所以也称为静态工厂
public class SimpleFactory {
public static Computer createComputer(String type){
if(type.equalsIgnoreCase("mac")){
return new MacComputer();
}else{
return new LenovoComputer();
}
}
public static Computer createLenovoComputer(){
return new LenovoComputer();
}
public static Computer createMacComputer(){
return new MacComputer();
}
}
--------------------------------------------------------------------------------
//客户端
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Computer c1 = simpleFactory.createComputer("mac");
Computer c2 = simpleFactory.createLenovoComputer();
c1.play();
c2.play();
}
}
三、工厂方法模式
3.1、作用:为了避免简单工厂模式的缺点,不完全满足开闭原则,而设计的。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类
3.2、缺点:除了是满足开闭原则外,从结构复杂度、代码复杂的、客户端编程难度、管理上的难度上看,工厂方法相比简单工厂都要复杂,所以实际上,我们一般只会用简单工厂,很少会用到工厂方法模式
3.3、UML图:
3.4、示例:
//电脑接口
public interface Computer {
public void play();
}
//联想电脑类
class LenovoComputer implements Computer{
public void play() {
System.out.println("我是联想电脑");
}
}
//苹果电脑类
class MacComputer implements Computer{
public void play() {
System.out.println("我是苹果电脑");
}
}
--------------------------------------------------------------------------------
//工厂接口
public interface FactoryMethod {
public Computer createComputer();
}
//联想工厂
class LenovoComputerFactory implements FactoryMethod{
public Computer createComputer() {
return new LenovoComputer();
}
}
//苹果工厂
class MacComputerFactory implements FactoryMethod{
public Computer createComputer() {
return new MacComputer();
}
}
--------------------------------------------------------------------------------
//客户端
public class Client {
public static void main(String[] args) {
FactoryMethod factory1 = new LenovoComputerFactory();
FactoryMethod factory2 = new MacComputerFactory();
LenovoComputer c1 = (LenovoComputer) factory1.createComputer();
MacComputer c2 = (MacComputer) factory2.createComputer();
c1.play();
c2.play();
}
}
结论:由此看出,如果再加入一个华为的电脑,只需加入一个华为电脑的类和一个华为电脑工厂就可以了,不需要修改之前的任何代码,因此满足了开闭原则
四、抽象工厂模式
4.1、作用:用来生产不同产品族的全部产品,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
4.2、缺点:对于增加新的产品,无能为力;支持增加产品族(如在下面例子中,若再加入华为电脑,则需要新增华为电脑工厂类,并要修改每一个配件类,去新增产品配件,因此就不能满足开闭原则了)
4.3、UML图:
4.4、示例:
//电脑类
public class Computer {
private Engine engine;
private Keyboard keyboard;
private Mouse mouse;
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Keyboard getKeyboard() {
return keyboard;
}
public void setKeyboard(Keyboard keyboard) {
this.keyboard = keyboard;
}
public Mouse getMouse() {
return mouse;
}
public void setMouse(Mouse mouse) {
this.mouse = mouse;
}
public void play(){
System.out.println(engine.getName()+keyboard.getName()+mouse.getName());
};
}
//引擎类(为了简洁,此处没有用到接口,直接用名字代表不同的对象)
class Engine{
private String name;
public Engine(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//键盘类(为了简洁,此处没有用到接口,直接用名字代表不同的对象)
class Keyboard{
private String name;
public Keyboard(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//鼠标类(为了简洁,此处没有用到接口,直接用名字代表不同的对象)
class Mouse{
private String name;
public Mouse(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
--------------------------------------------------------------------------------
//抽象工厂接口
public interface AbstractFactory {
public Computer createComputer();
}
//联想电脑工厂
class LenovoComputerFactory implements AbstractFactory{
public Computer createComputer() {
Computer c = new Computer();
c.setEngine(new Engine("联想CPU"));
c.setKeyboard(new Keyboard("联想键盘"));
c.setMouse(new Mouse("联想鼠标"));
return c;
}
}
//苹果电脑工厂
class MacComputerFactory implements AbstractFactory{
public Computer createComputer() {
Computer c = new Computer();
c.setEngine(new Engine("苹果CPU"));
c.setKeyboard(new Keyboard("苹果键盘"));
c.setMouse(new Mouse("苹果鼠标"));
return c;
}
}
--------------------------------------------------------------------------------
//客户端
public class Client {
public static void main(String[] args) {
AbstractFactory Lenovofactory = new LenovoComputerFactory();
AbstractFactory Macfactory = new MacComputerFactory();
Computer LenovoComputer = Lenovofactory.createComputer();
Computer MacComputer = Macfactory.createComputer();
LenovoComputer.play();
MacComputer.play();
}
}
五、建造者模式
5.1、作用:分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。 从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
5.2、UML图:
5.3、示例:
//电脑类
public class Computer {
private Engine engine;
private Keyboard keyboard;
private Mouse mouse;
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Keyboard getKeyboard() {
return keyboard;
}
public void setKeyboard(Keyboard keyboard) {
this.keyboard = keyboard;
}
public Mouse getMouse() {
return mouse;
}
public void setMouse(Mouse mouse) {
this.mouse = mouse;
}
public void play(){
System.out.println(engine.getName()+keyboard.getName()+mouse.getName());
};
}
//引擎类
class Engine{
private String name;
public Engine(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//键盘类
class Keyboard{
private String name;
public Keyboard(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//鼠标类
class Mouse{
private String name;
public Mouse(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
--------------------------------------------------------------------------------
//电脑构建接口
public interface ComputerBuilder {
public Engine createEngine();
public Keyboard createKeyBoard();
public Mouse createMouse();
}
class realComputerBuilder implements ComputerBuilder{
public Engine createEngine() {
return new Engine("联想CPU");
}
public Keyboard createKeyBoard() {
return new Keyboard("苹果键盘");
}
public Mouse createMouse() {
return new Mouse("华为鼠标");
}
}
--------------------------------------------------------------------------------
//电脑组装接口
public interface ComputerDirector {
public Computer computerDirector(ComputerBuilder computerBuilder);
}
class realComputerDirector implements ComputerDirector{
public Computer computerDirector(ComputerBuilder computerBuilder) {
Computer c = new Computer();
c.setEngine(computerBuilder.createEngine());
c.setKeyboard(computerBuilder.createKeyBoard());
c.setMouse(computerBuilder.createMouse());
return c;
}
}
--------------------------------------------------------------------------------
//客户端
public class Client {
public static void main(String[] args) {
ComputerBuilder computerBuilder = new realComputerBuilder();
ComputerDirector computerDirector = new realComputerDirector();
Computer c =computerDirector.computerDirector(computerBuilder);
System.out.println(c.getEngine().getName()+""+c.getKeyboard().getName()+""+c.getMouse().getName()+"");
}
}
--------------------------------------------------------------------------------
总结:如上代码,如果要生产不同型号的配件,只需要创建一个新的构造器类,需要组装成不同型号的电脑也只需要创建一个新的组黄器类,不需要修改之前的代码
六、原型模式(prototype)
6.1、作用:通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。它本身就是java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点。优势有:效率高(直接克隆,避免了重新执行构造过程步骤) 。克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。
6.2、重要知识点:克隆问题(浅克隆和深克隆,此处不详说,请另行查看我的文章:https://www.jianshu.com/p/d2783a7cf031)
6.3、UML图:
6.4、示例:
//电脑类
public class Computer implements Serializable{
private Engine engine;
private Keyboard keyboard;
private Mouse mouse;
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Keyboard getKeyboard() {
return keyboard;
}
public void setKeyboard(Keyboard keyboard) {
this.keyboard = keyboard;
}
public Mouse getMouse() {
return mouse;
}
public void setMouse(Mouse mouse) {
this.mouse = mouse;
}
public void play(){
System.out.println(engine.getName()+keyboard.getName()+mouse.getName());
};
}
//引擎类
class Engine implements Serializable{
private String name;
public Engine(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//键盘类
class Keyboard implements Serializable{
private String name;
public Keyboard(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//鼠标类
class Mouse implements Serializable{
private String name;
public Mouse(String name){this.name=name;}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
--------------------------------------------------------------------------------
public class ComputerClone {
public Computer clone(Computer c){
Computer c2 =null;
try {
ByteArrayOutputStream bas =new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bas);
oos.writeObject(c);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bas.toByteArray()));
c2 =(Computer) ois.readObject();
bas.flush();oos.flush();
bas.close();oos.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return c2;
}
}
--------------------------------------------------------------------------------
public class Client {
public static void main(String[] args) {
ComputerClone computerClone = new ComputerClone();
Computer c = new Computer();
c.setEngine(new Engine("华为CPU"));
c.setKeyboard(new Keyboard("联想键盘"));
c.setMouse(new Mouse("苹果鼠标"));
Computer c2 = computerClone.clone(c);
System.out.println(c);
System.out.println(c2);
System.out.println(c2.getEngine().getName()+""+c2.getKeyboard().getName()+""+c2.getMouse().getName()+"");
}
}
--------------------------------------------------------------------------------
结果:
com.primeton.GOF23.prototype.Computer@6727734f
com.primeton.GOF23.prototype.Computer@7176c74b
华为CPU联想键盘苹果鼠标
--------------------------------------------------------------------------------
总结:通过结果可以看到克隆出来的对象是一个新对象,但其中属性与原型对象一致,整个新对象产生的过程很简单,效率高(直接克隆,避免了重新执行构造过程步骤)