设计模式
单例模式
饿汉式静态常量
class Singleton {
private Singleton(){
}
private final static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
饿汉式静态代码块
class Singleton {
private Singleton(){
}
private static Singleton instance;
static {
instance = new Singleton();
}
public static Singleton getInstance() {
return instance;
}
}
以上两种写法的优缺点一致:
优点:这两种写法比较简单,就是在类装载的时候完成实例化,避免了线程同步问题。
缺点:没有达到懒加载的效果,有可能会造成内存空间的浪费。
懒汉式(线程不安全)
class Singleton {
private Singleton(){
}
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
起到了懒加载的效果,但只能在单线程环境下使用,多线程环境下可能会产生多个实例。在实际开发当中,不要使用这种方式。
懒汉式(线程安全,同步方法)
class Singleton {
private Singleton(){
}
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
有点:解决了线程不安全的问题。
缺点:效率太低了,每个线程想获得类实例的时候都要执行一次同步方法。在实际开发中不推荐使用。
懒汉式(线程安全,同步代码,双重检查)
class Singleton {
private Singleton(){
}
/**
* 加入volatile,保证线程间可见性
*/
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查是多线程开发中常使用到的,这样实例化的代码就只需要执行一次,避免产生多个实例。而且线程安全,懒加载,效率较高。实际开发中,推荐使用这种单例设计模式。
懒汉式(静态内部类)
class Singleton {
private Singleton(){
}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
采用类装载机制来保证初始化实例时只有一个线程。
静态内部类在外部类被装载时并不会立即初始化,而是在需要时才会装载静态内部类,达到了懒加载的效果。
而且类的静态属性只会在第一次装载类的时候初始化,所以是JVM帮我们保证了线程的安全性,在初始化时,别的线程是无法进入。
总结:避免了线程不安全,利用静态内部类的特点实现懒加载,效率高。推荐使用。
懒汉式(枚举)
public class Singleton07 {
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton1 == singleton2);
singleton1.sayhello();
}
}
enum Singleton {
INSTANCE;
public void sayhello() {
System.out.println("hello");
}
}
借助JDK1.5中添加的枚举来实现单例模式,不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象。
这种方式也是一个Java作者提倡的。推荐使用。
策略模式
封装不同地执行策略或者说方式,减少 if else 的使用。
策略的三大角色
- 环境角色类(策略的对象):比如价格有很多种计算方式,一种方式就是一种策略,价格就是策略的最终对象。(谁拥有策略)
- 抽象策略角色(策略的抽象类):一般是接口或者抽象类。
- 具体策略角色(策略实现类):包装了某一种策略的具体实现(行为)。
使用场景
假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。
根据描述,折扣是根据以下的几个算法中的一个进行的:
算法一:对初级会员没有折扣。
算法二:对中级会员提供10%的促销折扣。
算法三:对高级会员提供20%的促销折扣。
定义一个计算折扣的接口
public interface MemberStrategy {
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double calcPrice(double booksPrice);
}
用不同的折扣策略实现该接口
初级会员折扣类
public class PrimaryMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于初级会员的没有折扣");
return booksPrice;
}
}
中级会员折扣类
public class IntermediateMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于中级会员的折扣为10%");
return booksPrice * 0.9;
}
}
高级会员折扣类
public class AdvancedMemberStrategy implements MemberStrategy {
@Override
public double calcPrice(double booksPrice) {
System.out.println("对于高级会员的折扣为20%");
return booksPrice * 0.8;
}
}
策略对象类(环境角色)
public class Price {
//持有一个具体的策略对象
private MemberStrategy strategy;
/**
* 构造函数,传入一个具体的策略对象
* @param strategy 具体的策略对象
*/
public Price(MemberStrategy strategy){
this.strategy = strategy;
}
/**
* 计算图书的价格
* @param booksPrice 图书的原价
* @return 计算出打折后的价格
*/
public double quote(double booksPrice){
return this.strategy.calcPrice(booksPrice);
}
}
客户端调用
public class Client {
public static void main(String[] args) {
//选择并创建需要使用的策略对象
MemberStrategy strategy = new AdvancedMemberStrategy();
//创建环境
Price price = new Price(strategy);
//计算价格
double quote = price.quote(300);
System.out.println("图书的最终价格为:" + quote);
}
}
策略模式的优缺点
优点:可以避免使用多重条件(if-else)语句。
缺点:
- 客户端必须要知道所有策略,并自行决定使用哪一种策略。
- 如果策略比较多,对象的数量也会很多。
