简单和常用的设计模式的Java实现

 

1、单例模式

单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。

因为这个类只有一个实例,因此,自然不能让调用方使用new Xyz()来创建实例了。所以,单例的构造方法必须是private,这样就防止了调用方自己创建实例,但是在类的内部,是可以用一个静态字段来引用唯一创建的实例的:

单例模式的实现方式很简单:

  1. 只有private构造方法,确保外部无法实例化;
  2. 通过private static变量持有唯一实例,保证全局唯一性;
  3. 通过public static方法返回此唯一实例,使外部调用方能获取到实例。

方式一:饿汉模式

初始化的时候,就创建了唯一的实例,不管是否需要用到。不需要自己加同步,一定产生唯一的实例。

public class Singleton {
    // 静态字段引用唯一实例:
    private static final Singleton INSTANCE = new Singleton();

    // 通过静态方法返回实例:
    public static Singleton getInstance() {
        return INSTANCE;
    }

    // private构造方法保证外部无法实例化:
    private Singleton() {
    }
}

方式二:懒汉模式

或者直接把static变量暴露给外部:

初始化类的时候,不创建唯一的实例,而是等到真正需要用到的时候才创建。必须加上同步,否则有可能依然创建多个实例。

public class Singleton {
    private static Singleton instance;  
    private Singleton (){}  

    public static Singleton getInstance() {  //这是非线程安全的,在static后面加synchronized就是线程安全的
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }
}

方式三:双重检验

因为不加synchronized会导致多线程环境创建多个对象,但是如果加了线程锁,会降低系统性能,所以先在前面加个判断,两者折衷。

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  

 

Java标准库有一些类就是单例,例如Runtime这个类:

Runtime runtime = Runtime.getRuntime();

2、观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式(Observer)又称发布-订阅模式(Publish-Subscribe:Pub/Sub)。它是一种通知机制,让发送通知的一方(被观察方)和接收通知的一方(观察者)能彼此分离,互不影响。

public class Store {
    private List<ProductObserver> observers = new ArrayList<>();
    private Map<String, Product> products = new HashMap<>();

    // 注册观察者:
    public void addObserver(ProductObserver observer) {
        this.observers.add(observer);
    }

    // 取消注册:
    public void removeObserver(ProductObserver observer) {
        this.observers.remove(observer);
    }

    public void addNewProduct(String name, double price) {
        Product p = new Product(name, price);
        products.put(p.getName(), p);
        // 通知观察者:
        observers.forEach(o -> o.onPublished(p));
    }

    public void setProductPrice(String name, double price) {
        Product p = products.get(name);
        p.setPrice(price);
        // 通知观察者:
        observers.forEach(o -> o.onPriceChanged(p));
    }
}

观察者类型就可以无限扩充,而且,观察者的定义可以放到客户端:

// observer:
Admin a = new Admin();
Customer c = new Customer();
// store:
Store store = new Store();
// 注册观察者:
store.addObserver(a);
store.addObserver(c);

3、装饰者模式

动态地给一个对象添加一些额外的职责。就增加功能来说,相比生成子类更为灵活。

装饰器(Decorator)模式,是一种在运行期动态给某个对象的实例增加功能的方法。

public interface TextNode {
    // 设置text:
    void setText(String text);
    // 获取text:
    String getText();
}

对于核心节点,例如<span>,它需要从TextNode直接继承:

public class SpanNode implements TextNode {
    private String text;

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return "<span>" + text + "</span>";
    }
}

紧接着,为了实现Decorator模式,需要有一个抽象的Decorator类:

public abstract class NodeDecorator implements TextNode {
    protected final TextNode target;

    protected NodeDecorator(TextNode target) {
        this.target = target;
    }

    public void setText(String text) {
        this.target.setText(text);
    }
}

这个NodeDecorator类的核心是持有一个TextNode,即将要把功能附加到的TextNode实例。接下来就可以写一个加粗功能:

public class BoldDecorator extends NodeDecorator {
    public BoldDecorator(TextNode target) {
        super(target);
    }

    public String getText() {
        return "<b>" + target.getText() + "</b>";
    }
}

类似的,可以继续加ItalicDecoratorUnderlineDecorator等。客户端可以自由组合这些Decorator:

TextNode n1 = new SpanNode();
TextNode n2 = new BoldDecorator(new UnderlineDecorator(new SpanNode()));
TextNode n3 = new ItalicDecorator(new BoldDecorator(new SpanNode()));
n1.setText("Hello");
n2.setText("Decorated");
n3.setText("World");
System.out.println(n1.getText());
// 输出<span>Hello</span>

System.out.println(n2.getText());
// 输出<b><u><span>Decorated</span></u></b>

System.out.println(n3.getText());
// 输出<i><b><span>World</span></b></i>

4、代理模式

为其他对象提供一种代理以控制对这个对象的访问。

4.1 静态代理

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

接口:

public interface HelloInterface {
    void sayHello();
}
 


被代理类:

public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello zhanghao!");
    }
}


代理类:

public class HelloProxy implements HelloInterface{
    private HelloInterface helloInterface = new Hello();
    @Override
    public void sayHello() {
        System.out.println("Before invoke sayHello" );
        helloInterface.sayHello();
        System.out.println("After invoke sayHello");
    }
}


代理类调用:
被代理类被传递给了代理类HelloProxy,代理类在执行具体方法时通过所持用的被代理类完成调用。

    public static void main(String[] args) {
        HelloProxy helloProxy = new HelloProxy();
        helloProxy.sayHello();
    }


    输出:

Before invoke sayHello
Hello zhanghao!
After invoke sayHello


 使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。
 

 

4.2 动态代理

①定义接口

public interface HelloImplements {
    void sayHello();
}

②实现接口(作为被代理类)

public class Hello implements HelloImplements {
    
    @Override
    public void sayHello() {
        System.out.println("被代理对象打印");
    }
}

③动态代理类

public class ProxyHello implements InvocationHandler {

    private Object delegate;

    public ProxyHello(Object object){
        this.delegate = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理先打印");
        method.invoke(delegate,args);
        System.out.println("动态代理打印结束");
        return null;
    }
}

 ④测试类中调用

public class TestProxyHello {

    public static void main(String[] args) {

        IHello hello = new Hello();

        InvocationHandler proxyHello = new ProxyHello(hello);

        IHello iHello = (IHello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), proxyHello);
        iHello.sayHello();
    }
}

5、工厂方法

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。

工厂方法即Factory Method,是一种对象创建型模式。

工厂方法的目的是使得创建对象和使用对象是分离的,并且客户端总是引用抽象工厂和抽象产品:

通常我们会在接口Factory中定义一个静态方法getFactory()来返回真正的子类:

public interface NumberFactory {
    // 创建方法:
    Number parse(String s);

    // 获取工厂实例:
    static NumberFactory getFactory() {
        return impl;
    }

    static NumberFactory impl = new NumberFactoryImpl();//注意这里返回的已经是接口的实现类的对象

}

在客户端中,我们只需要和工厂接口NumberFactory以及抽象产品Number打交道:

NumberFactory factory = NumberFactory.getFactory();
Number result = factory.parse("123.456");

调用方可以完全忽略真正的工厂NumberFactoryImpl和实际的产品BigDecimal,这样做的好处是允许创建产品的代码独立地变换,而不会影响到调用方。

有的童鞋会问:一个简单的parse()需要写这么复杂的工厂吗?实际上大多数情况下我们并不需要抽象工厂,而是通过静态方法直接返回产品,即:

public class NumberFactory {
    public static Number parse(String s) {
        return new BigDecimal(s);
    }
}

6、抽象工厂

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式(Abstract Factory)是一个比较复杂的创建型模式。

抽象工厂模式和工厂方法不太一样,它要解决的问题比较复杂,不但工厂是抽象的,产品是抽象的,而且有多个产品需要创建,因此,这个抽象工厂会对应到多个实际工厂,每个实际工厂负责创建多个实际产品:

7、生产者-消费者模式

生产者-消费者模式,生产者生产商品,然后通知消费者进行消费。

①定义生产者

public class Vendor {
	// 定义库存数量
	private int count;

	// 定义最大库存
	private final int MAX_COUNT = 10;

	public synchronized void production() {
		while (count >= MAX_COUNT) {
			try {
				System.out.println(Thread.currentThread().getName() + "库存数量达到最大值,停止生产。");
				// 此时生产线程全部进入等待状态
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 否则生产商品
		count++;
		System.out.println(Thread.currentThread().getName() + "正在生产商品,当前库存为:" + count);
		notifyAll();
	}

	public synchronized void consumers() {
		while (count <= 0) {
			try {
				System.out.println(Thread.currentThread().getName() + "没有商品了,消费者处于等待状态...");
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		count--;
		System.out.println(Thread.currentThread().getName() + "正在消费,当前库存为:" + count);
		notifyAll();
	}
}

②分别定义两条线程。

public class SetTarget implements Runnable {

	private Vendor vendor;

	public SetTarget(Vendor vendor) {
		this.vendor = vendor;
	}

	@Override
	public void run() {
		while(true){
			vendor.production();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public class GetTarget implements Runnable {

	private Vendor vendor;

	public GetTarget(Vendor vendor) {
		this.vendor = vendor;
	}

	@Override
	public void run() {
		while(true){
			vendor.consumers();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

③主方法。

public class Demo {
	public static void main(String[] args) {
		Vendor vendor = new Vendor();
		SetTarget set = new SetTarget(vendor);
		GetTarget get = new GetTarget(vendor);
		
		// 开启线程生产商品
		new Thread(set).start();
		new Thread(set).start();
		new Thread(set).start();
		new Thread(set).start();
		
		// 开启消费者线程
		new Thread(get).start();
	}
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值