当前位置 博文首页 > 不负_韶华的博客:心得篇(二)——设计模式之创建型模式
github源码地址
设计模式是前人结合实践总结出来的一套行之有效的理论,能够将实际业务需求转换为技术实现时,使系统更具有维护性、扩展性。在学习设计模式时,我不禁赞叹它巧妙的思路和优雅的方式。
在学习完设计模式之后,强烈推荐解读一个把设计模式使用得淋漓尽致的经典框架的源码——Spring,我也会在设计模式之后去更新它。
本章节讲解创建型模式包括工厂模式、单例模式、原型模式和建造者模式。
1. 简单工厂模式 Simple-Factory-Pattern
简单工厂模式,由一个工厂对象决定一类产品的实例的创建,它不属于GOF23种设计模式。简单工厂适用于工厂类负责创建的对象较少的场景(小场面建个小作坊就够了),客户端只需要传入工厂类的参数,不参与创建对象的逻辑。
缺点:工厂类的权责过大,不易于拓展复杂的产品结构
示例: 定义Npc接口和MageNpc类、WarriorNpc类
public interface INpc {
void fight();
}
public class MageNpc implements INpc {
@Override
public void fight() {
System.out.println("使用魔法攻击");
}
}
public class WarriorNpc implements INpc {
@Override
public void fight() {
System.out.println("使用物理攻击");
}
}
NpcSimpleFactory中的create方法通过反射创建npc实例
public class NpcSimpleFactory {
public static INpc createNpc(Class<? extends INpc> clazz){
try {
if (clazz != null){
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public class TestSimpleFactory {
public static void main(String[] args) {
INpc npc1 = NpcSimpleFactory.createNpc(MageNpc.class);
INpc npc2 = NpcSimpleFactory.createNpc(WarriorNpc.class);
if (npc1 != null) {
npc1.fight();
}
if (npc2 != null) {
npc2.fight();
}
}
}
类图结构
回顾示例中的工厂类NpcSimpleFactory,有一种万能工厂的感觉,如果每个Npc创建的逻辑有差异的话,其职责会越来越大,破坏了单一职责原则。
2. 工厂方法模式 Factory-Method-Pattern
工厂方法模式指定义一个创建对象的接口,其实现类决定实例化那个类。使用方只需要关心使用哪个工厂,无需关心创建细节,这样新加入的产品符合开闭原则。
缺点:增加了系统的抽象性和复杂度(比如打地基需要成本,不建高楼地基也没必要打那么深)
示例:以刚才讲的简单工厂中的Npc为类,对NpcSimpleFactory 进行抽象
public interface INpcMethodFactory {
INpc create();
}
public class MageNpcMethodFactory implements INpcMethodFactory{
@Override
public INpc create() {
return new MageNpc();
}
}
public class WarriorNpcMethodFactory implements INpcMethodFactory{
@Override
public INpc create() {
return new WarriorNpc();
}
}
public class TestMethodFactory {
public static void main(String[] args) {
INpcMethodFactory factory = new WarriorNpcMethodFactory();
factory.create().fight();
}
}
示例类图:
源码之中处处可见这种设计模式,比如slf4j的LogFactory中的getLogger方法就使用了工厂方法模式(tip:此处同时还使用了单例模式和建造者模式决定使用哪个工厂实现类)
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
3. 抽象工厂模式 Abastract-Factory-Pattern
抽象工厂模式指提供一个创建一系列相关或依赖对象的接口,强调的是同系列对象创建时大量重复的代码,使用方不依赖具体实现。
抽象工厂模式是在Spring中应用的最为广泛的一种设计模式,它是理论与经验的碰撞和取舍。与工厂方法模式相比,它的复杂度更多,同时还牺牲了一定的开闭原则,但随着系统复杂度达到一定程度后,抽象工厂模式能够清晰地描述有关联又不是同一类的对象的构造关系,易于扩展。
示例:Npc和装备Equipment类、技能Skill类属于同族,构建抽象工厂
public interface IEquipment {
void note();
}
public interface ISkill {
void note();
}
public abstract class NpcAbstractFactory {
protected void init(){
System.out.println("初始化资源");
}
protected abstract IEquipment createEquipment();
protected abstract ISkill createSkill();
}
示例类图
基本饿汉式单例:
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return INSTANCE;
}
}
public class HungrySingletonTest {
public static void main(String[] args) {
new Thread(()->{
HungrySingleton instance = HungrySingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}).start();
new Thread(()->{
HungrySingleton instance = HungrySingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}).start();
}
}
饿汉式指在类加载时就完成了对象的实例化,适用于少量单例的情况。优点是线程绝对安全,缺点是内存浪费,“占着茅坑不拉屎”。
基本懒汉式单例:
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}).start();
new Thread(()->{
LazySingleton instance = LazySingleton.getInstance();
System.out.println(Thread.currentThread().getName() + ":" + instance);
}).start();
}
}
设置线程debug模式,两个线程获取到了不同的单例
懒汉式指在类初始化时不进行实例化,当使用到单例对象时才进行创建,解决了饿汉式内存浪费的问题,但同时造成了线程问题
为解决懒汉模式的线程安全问题,需要加synchronized锁,但如果在整个方法上加同步锁,势必会造成大量线程的访问阻塞,于是便有了经典的
double-check单例:
public class DoubleCheckSingleton