创建型模式
创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。
工厂模式分为简单工厂模式,工厂模式,抽象工厂模式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。本质就是使用工厂方法代替new操作。
简单工厂模式
public class FoodFactory {
public static Food makeFood(String name) {
if (name.equals("兰州拉面")) {
Food noodle = new LanZhouNoodle();
System.out.println("兰州拉面"+noodle+"出锅啦");
return noodle;
} else if (name.equals("黄焖鸡")) {
Food chicken = new HuangMenChicken();
System.out.println("黄焖鸡"+ chicken +"出锅啦");
return chicken;
} else {
System.out.println("不知道你做的什么哦~");
return null;
}
}
}
其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。
public class Cook {
public static void main(String[] args) {
Food food = FoodFactory.makeFood("黄焖鸡");
FoodFactory.makeFood("jaja");
}
}
简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。
我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。
在此例中可以看出,Cook 类在使用 FoodFactory 时就不需要 new 任何一个对象,这就是简单工厂模式的好处,封装了 new 的部分,做到的代码易用性。
工厂模式
简单工厂模式很简单,如果它能满足我们的需要,我觉得就不要折腾了。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂,也就是有多个实际工厂,每个工厂用于返回这一类的实际对象
public interface FoodFactory {
Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {
@Override
public Food makeFood(String name) {
if (name.equals("A")) {
return new ChineseFoodA();
} else if (name.equals("B")) {
return new ChineseFoodB();
} else {
return null;
}
}
}
public class AmericanFoodFactory implements FoodFactory {
@Override
public Food makeFood(String name) {
if (name.equals("A")) {
return new AmericanFoodA();
} else if (name.equals("B")) {
return new AmericanFoodB();
} else {
return null;
}
}
}
其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food, 与此同时有两个工厂 ChineseFoodFactory 以及 AmericanFoodFactory 用于分别创建前面四种实例对象。
public class APP {
public static void main(String[] args) {
// 先选择一个具体的工厂
FoodFactory factory = new ChineseFoodFactory();
// 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象
Food food = factory.makeFood("A");
}
}
虽然都是调用 makeFood("A") 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。
第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。
核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。
抽象工厂模式
这个模式就是在工厂模式的基础上,进一步第一层的多个工厂进行抽象。
为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
抽象工厂一般由以下四个部分组成。
AbstractFactory 声明一个创建抽象产品对象的操作接口。
ConcreteFactory 实现创建具体产品对象的操作。
AbstractProduct 为一类产品对象声明一个接口。
ConcreteProduct 定义一个将被相应的具体工厂创建的产品对象。 实现AbstractProduct接口。
Client 仅使用由AbstractFactory和AbstractProduct类声明的接口
AbstractFactory:定义抽象工程类IAnimalFactory
抽象工厂包含抽象方法,需要被实例化。
package com.pizilei.design.abstractfactory;
/**
* 这个接口就是类图中标识的
* AbstractFactory抽象工厂
* @author pizilei
*
*/
public interface IAnimalFactory {
/**
* 定义创建Icat接口实例的方法
* @return
*/
ICat createCat();
/**
* 定义创建IDog接口实例的方法
* @return
*/
IDog createDog();
}
ConcreteFactory:创建抽象工厂类的两个实现类,WhiteAnimalFactory和BlackAnimalFactory
package com.pizilei.design.abstractfactory;
/**
* IAnimalFactory抽象工厂的实现类
* @author pizilei
*
*/
public class WhiteAnimalFactory implements IAnimalFactory {
@Override
public ICat createCat() {
return new WhiteCat();
}
@Override
public IDog createDog() {
return new WhiteDog();
}
}
package com.pizilei.design.abstractfactory;
/**
* IAnimalFactory抽象工厂的实现类
* @author pizilei
*/
public class BlackAnimalFactory implements IAnimalFactory {
@Override
public ICat createCat() {
return new BlackCat();
}
@Override
public IDog createDog() {
return new BlackDog();
}
}
AbstractProduct:定义抽象工厂中要生产的抽象产品接口ICat和IDog
package com.pizilei.design.abstractfactory;
/**
* 类图中定义的AbstractProduct
* 指定工厂生产的产品
* @author pizilei
*
*/
public interface ICat {
/**
* 定义方法
*/
void eat();
}
package com.pizilei.design.abstractfactory;
/**
* 类图中定义的AbstractProduct
* 指定工厂生产的产品
* @author pizilei
*
*/
public interface IDog {
/**
* 定义方法
*/
void eat();
}
ConcreteProduct:创建产品的实现类BlackCat、BlackDog、WhiteCat、WhiteDog
package com.pizilei.design.abstractfactory;
/**
* ICat接口的实现类
* @author pizilei
*
*/
public class BlackCat implements ICat {
@Override
public void eat() {
System.out.println("The black cat is eating!");
}
}
package com.pizilei.design.abstractfactory;
/**
* IDog的实现类
* @author pizilei
*/
public class BlackDog implements IDog {
@Override
public void eat() {
System.out.println("The black dog is eating");
}
}
package com.pizilei.design.abstractfactory;
/**
* ICat的实现类
* @author pizilei
*
*/
public class WhiteCat implements ICat {
@Override
public void eat() {
System.out.println("The white cat is eating!");
}
}
package com.pizilei.design.abstractfactory;
/**
* IDog的实现类
* @author binghe
*
*/
public class WhiteDog implements IDog {
@Override
public void eat() {
System.out.println("The white dog is eating!");
}
}
Client:定义一个测试类Test
/**
* 测试类
* @author pizilei
*
*/
public class Test {
public static void main(String[] args) {
IAnimalFactory blackAnimalFactory = new BlackAnimalFactory();
ICat blackCat = blackAnimalFactory.createCat();
blackCat.eat();
IDog blackDog = blackAnimalFactory.createDog();
blackDog.eat();
IAnimalFactory whiteAnimalFactory = new WhiteAnimalFactory();
ICat whiteCat = whiteAnimalFactory.createCat();
whiteCat.eat();
IDog whiteDog = whiteAnimalFactory.createDog();
whiteDog.eat();
}
}
由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化
单例模式
简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。
getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。
特点:
- 类构造器私有
- 持有自己类型的属性
- 对外提供获取实例的静态方法
饿汉式写法:
public class Singleton {
private static Singleton instance = new Singleton(); // 缺点 会提前生成这个对象 导致内存占用
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
弊端:因为类加载的时候就会创建对象,所以有的时候还不需要使用对象,就会创建对象,造成内存的浪费
饱汉模式最容易出错:
public class Singleton {
// 首先,也是先堵死 new Singleton() 这条路
private Singleton() {}
// 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
private static volatile Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
// 加锁
synchronized (Singleton.class) {
// 这一次判断也是必须的,不然会有并发问题
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查,指的是两次检查 instance 是否为 null。
volatile 在这里是需要的,希望能引起读者的关注。
很多人不知道怎么写,直接就在 getInstance() 方法签名上加上 synchronized,这就不多说了,性能太差。
public class Singleton {
private Singleton() {}
// 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
private static class Holder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}