网站首页 文章专栏 java设计模式之单例模式与装饰者模式
前言 :java中设计模式经过一代又一代前辈的打磨下,提取出了23种,大体分为三类:创建型模式(5种),结构型模式(7种),行为型模式(11种),今天主要说一下里面的单例模式和装饰者模式。 |
一,设计模式遵循的原则有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(); } }
看来是可以的。
通过继承就可以给小明增加天赋了,每次继承就给他装饰一下,增加一个天赋,是通过继承父类的方式,重写父类的方法,加上自己的装饰,强化相应的功能。但是这样会出现当小明天赋越来越多时,会不停的继承,会很乱,而且如来再来一个小星也获得了唱歌的天赋,代码就重复了。所以这样虽然实现了装饰,但是效果不好。
如果修饰者修饰过后,可以将这个修饰过的结果传给下一个修饰者,这样的话,就不需要定义这个么多的组合类了,只需要定义相应类型的修饰者就可以了。如果有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(); } }
结果如下:
通过这种装饰方式,我们可以很方便地根据具体情况增加修饰者,并且不会产生多余的子类,整个过程很灵活,不像单纯使用类继承的方式会产生很多子类。
People :定义了一个可以被动态添加功能的接口
Arts :持有一个People对象的引用,并且定义了一个和People保持一致的接口。
SingTalent :为People添加功能的角色。
优点:
给一个对象动态地、透明地添加职能,即:不影响其他对象。
动态添加的职能能够被取消。
版权声明:本文由星尘阁原创出品,转载请注明出处!