网站首页 文章专栏 java设计模式之单例模式与装饰者模式
java设计模式之单例模式与装饰者模式

java设计模式之单例模式与装饰者模式



 前言 :java中设计模式经过一代又一代前辈的打磨下,提取出了23种,大体分为三类:创建型模式(5种),结构型模式(7种),行为型模式(11种),今天主要说一下里面的单例模式和装饰者模式。


timg (4).jpg


一,设计模式遵循的原则有6个:


1、开闭原则(Open Close Principle)

  对扩展开放,对修改关闭。

2、里氏代换原则(Liskov Substitution Principle)

  只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

3、依赖倒转原则(Dependence Inversion Principle)

  这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

  使用多个隔离的借口来降低耦合度。

5、迪米特法则(最少知道原则)(Demeter Principle)

  一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

  原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。


二,单例模式


单例模式由分为懒汉式和饿汉式,其中懒汉式又涉及多线程并发问题,下面直接写出代码。

1),饿汉式

public class Singleton {    
    private Singleton1() {}    
    private static Singleton1 single = new Singleton1();    
    public static Singleton getSingletonInstance() {        
        return single;
    }
}


2),懒汉式

public class Singleton {
	
	private Singleton(){}
	
	private static volatile Singleton singleton = null;		// volatile保证singleton的多线程可见性
	
	public static Singleton getSingletonInstance(){

		if(singleton == null){		   // 第一次判断是否为null,有必要,里面有锁,防止下一个线程想要获取对象,必须等待上一个线程释放锁之后,才可以继续运行
			synchronized (Singleton.class) {		   // 加锁,防止多线程问题
				if(singleton == null){		
					singleton = new Singleton();
				}
			}
		}
		return singleton;
	}

}



三,装饰者模式



装饰者模式:动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案。



在程序设计时,我们往往会给一个对象的功能加上一些修饰,对原始的功能进行拓展和增强,以来满足我们的需求。


比如我们现在有一个人的类,每个人都有生来的天赋,但是随着人的长大,会慢慢获得一些天赋,我们用装饰者模式演示下。


1),第一种实现

/**
 * 定义人类
 */
public class People {
	
	/**
	 * 每个人都有天赋
	 */
	public void talent(){
		System.out.println("人类天赋之一,会说话!");
	}

}


然后有个人小明,他长大后获得了唱歌的天赋,我们可以用继承People来获取天赋,并加上唱歌的天赋。

public class XiaoMingTalent extends People{

	@Override
	public void talent() {
		super.talent();
		sing();
	}
	
	public void sing() {
		System.out.println("获得了唱歌天赋!");
	}
}


随着时间流逝,小明又获得了跳舞的天赋,我们不得不再继承一下。

public class XiaoMingTalent2 extends XiaoMingTalent{

	@Override
	public void talent() {
		super.talent();
		dance();
	}
	
	public void dance() {
		System.out.println("获得了跳舞的天赋");
	}
}


测试一下,小明的天赋有没有获得。

public class TalentTest {

	public static void main(String[] args) {
		
		People xiaoming = new XiaoMingTalent2();
		xiaoming.talent();
	}
}

TIM图片20190824131748.png

看来是可以的。


通过继承就可以给小明增加天赋了,每次继承就给他装饰一下,增加一个天赋,是通过继承父类的方式,重写父类的方法,加上自己的装饰,强化相应的功能。但是这样会出现当小明天赋越来越多时,会不停的继承,会很乱,而且如来再来一个小星也获得了唱歌的天赋,代码就重复了。所以这样虽然实现了装饰,但是效果不好。

如果修饰者修饰过后,可以将这个修饰过的结果传给下一个修饰者,这样的话,就不需要定义这个么多的组合类了,只需要定义相应类型的修饰者就可以了。如果有10 个修饰者的话,只需要定义10 个修饰者的类,然后在实现的时候我们根据需求添加到相应的修饰就可以了。

从上面的的两个个装饰来看,无非是在原有 talent() 方法的基础上添加一些辅助的修饰的功能。

在上面的继承机制上,talent()方法在装饰者定义的时候已经是一个固定功能的了。如果可以将这个talent()方法变为动态的,即运行时确定的行为,然后再在修饰的时候加上相应的修饰就可以了。

一个类要动态绑定某个对象的行为,是持有相应的对象引用,然后在运行时根据这个引用绑定的具体对象,体现出不同的行为,这个就是动态绑定。


依照上面定义的规则,我们可以通过下面来实现,定义一个Arts类,持有一个People对象,只有当运行的时候动态绑定到talent() 方法。然后在这个talent() 方法的基础上进行装饰,加入相应的辅助、增强功能。

public class Arts extends People{

	public final People people;
	
	public Arts(People people) {
		super();
		this.people = people;
	}
	
	@Override
	public void talent(){
		people.talent();
	}
}


这样的话,如果我们在创建Arts对象的时候,如果传入的People 是已经被修饰过的,即talent()方法是被重写过的,那么我们就可以在此基础上再添新的修饰了。

        1>,唱歌天赋的装饰

public class SingTalent  extends Arts {

	public SingTalent(People people) {
		super(people);
	}
	
	@Override
	public void talent() {
		super.talent();
		this.sing();
	}
	
	public void sing(){
		System.out.println("通过修饰-->获得唱歌天赋");
	}

}

        

        2>,跳舞天赋的装饰

public class DanceTalent  extends Arts {

	public DanceTalent(People people) {
		super(people);
	}
	
	@Override
	public void talent() {
		super.talent();
		this.dance();
	}
	
	public void dance(){
		System.out.println("通过修饰-->获得跳舞天赋");
	}

}


以上我们就实现了装饰者模式,我们来测试下,看小明是不是获得了天赋。

public class TalentTest {

	public static void main(String[] args) {
		
		People xiaoming = new People();
		
		Arts arts = new Arts(xiaoming);
		System.out.println("未装饰之前");
		arts.talent();
		System.out.println("======================");
		System.out.println("第一次装饰唱歌");
		arts = new SingTalent(arts);
		arts.talent();
		System.out.println("======================");
		System.out.println("再次装饰跳舞");
		arts = new DanceTalent(arts);
		arts.talent();
		
	}
}

结果如下:

TIM图片20190824132212.png




通过这种装饰方式,我们可以很方便地根据具体情况增加修饰者,并且不会产生多余的子类,整个过程很灵活,不像单纯使用类继承的方式会产生很多子类。 

People :定义了一个可以被动态添加功能的接口 

Arts :持有一个People对象的引用,并且定义了一个和People保持一致的接口。 

SingTalent :为People添加功能的角色。


优点: 

给一个对象动态地、透明地添加职能,即:不影响其他对象。 

动态添加的职能能够被取消。




版权声明:本文由星尘阁原创出品,转载请注明出处!

本文链接:http://www.52xingchen.cn/detail/44




赞助本站,网站的发展离不开你们的支持!
来说两句吧
最新评论