Java设计模式整理
1.单例设计模式
所谓单例设计模式简单说就是无论程序如何运行,采用单例设计模式的类(Singleton类)永远只会有一个实例化对象产生,并且只提供一个全局的访问点 。
具体实现步骤如下:
- 将采用单例设计模式的类的构造方法私有化(采用private修饰);
- 在其内部产生该类的实例化对象,并将其封装成private static类型;
- 定义一个静态方法返回该类的实例。
public class SingletonTest {
private static SingletonTest instance;
private SingletonTest() {
}
public static SingletonTest getIstance() {
if (instance == null) {
synchronized (SingletonTest.class) {
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
}
这个模式有饿汉式、饱汉式等,不要管,记住上面面这个线性安全式就OK了。
2.工厂设计模式
有一个专门的类来负责创建实例的过程。 具体来说,把产品看着是一系列的类的集合,这些类是由某个抽象类或者接口派生出来的一个对象树。
而工厂类用来产生一个合适的对象来满足客户的要求。
如果简单工厂模式所涉及到的具体产品之间没有共同的逻辑,那么我们就可以使用接口来扮演抽象产品的角色;如果具体产品之间有功能的逻辑或,我们就必须把这些共同的东西提取出来,放在一个抽象类中,然后让具体产品继承抽象类。为实现更好复用的目的,共同的东西总是应该抽象出来的。
interface Animal { // 定义一个动物的接口
public void say(); // 说话方法
}
class Cat implements Animal { // 定义子类Cat
@Override
public void say() { // 覆写say()方法
System.out.println("我是猫咪,喵呜!");
}
}
class Dog implements Animal { // 定义子类Dog
@Override
public void say() { // 覆写say()方法
System.out.println("我是小狗,汪汪!");
}
}
class Factory { // 定义工厂类
public static Animal getInstance(String className) {
Animal a = null; // 定义接口对象
if ("Cat".equals(className)) { // 判断是哪个子类的标记
a = new Cat(); // 通过Cat子类实例化接口
}
if ("Dog".equals(className)) { // 判断是哪个子类的标记
a = new Dog(); // 通过Dog子类实例化接口
}
return a;
}
}
public class FactoryDemo {
public static void main(String[] args) {
Animal a = null; // 定义接口对象
a = Factory.getInstance(args[0]); // 通过工厂获取实例
if (a != null) { // 判断对象是否为空
a.say(); // 调用方法
}
}
}
还可以利用反射Class.forName(clz.getName()).newInstance()实现的简单工厂:
class Factory{
public static <T extends Animal > T createAnimal(Class<T> clz) {
T result = null;
try {
result = (T) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
这种方法“前辈程序员爸爸们”都说不好,“newInstance()调用的是无参构造函数生成对象,它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。”
老实说这个工厂模式原理挺简单,就是用一个“工厂”专门来创建来自相同父类的不同复杂子类产品。但是有静态工厂、简单工厂、工厂方法、抽象工厂等,挺烧脑。
静态:类+静态方法;
简单:抽象产品,由工厂来具体实例化所需具体产品;
工厂:抽象工厂,由具体工厂来具体实例化所需具体产品;
抽象:抽象工厂时,抽象产品体系?不理解。
一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象
工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。
3.建造者模式
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
1.Product(要创建的复杂对象):
public class Person {
private String head;
private String body;
private String foot;
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getFoot() {
return foot;
}
public void setFoot(String foot) {
this.foot = foot;
}
}
2.Builder(给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建)
public interface PersonBuilder {
void buildHead();
void buildBody();
void buildFoot();
Person buildPerson();//组装
}
3.ConcreteBuilder(实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例)
public class ManBuilder implements PersonBuilder {
Person person;
public ManBuilder() {
person = new Person();//创建一个person实例,用于调用set方法
}
public void buildBody() {
person.setBody("建造身体部分");
}
public void buildFoot() {
person.setFoot("建造四肢部分");
}
public void buildHead() {
person.setHead("建造头部部分");
}
public Person buildPerson() {
return person;//返回一个person实例
}
}
4.Director(调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建)
public class PersonDirector {
public Person constructPerson(PersonBuilder pb) {
//按照 身体--->头部--->四肢 的顺序创建人物
pb.buildHead();
pb.buildBody();
pb.buildFoot();
return pb.buildPerson();
}
}
5.测试
public class Test {
public static void main(String[] args) {
PersonDirector pd = new PersonDirector();
Person person = pd.constructPerson(new ManBuilder());
System.out.println(person.getBody());
System.out.println(person.getFoot());
System.out.println(person.getHead());
}
}
建造者模式与工厂模式的区别
我们可以看到,建造者模式与工厂模式是极为相似的,总体上,建造者模式仅仅只比工厂模式多了一个“导演类”的角色。在建造者模式的类图中,假如把这个导演类看做是最终调用的客户端,那么图中剩余的部分就可以看作是一个简单的工厂模式了。
与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。
另一种比较好看的建造者模式:
public class Person {
private String head;
private String body;
private String foot;
private Person (Builder builder) {
this.head= builder.head;
this.body= builder.body;
this.foot= builder.foot;
}
protected static class Builder {
private String head;
private String body;
private String foot;
protected Builder body(String head) {
this.head= head;
return this;
}
protected Builder body(String body) {
this.body= body;
return this;
}
protected Builder foot(String foot) {
this.foot= foot;
return this;
}
protected Person build() {
return new Person(this);
}
}
}
public class BuilderDemo {
public static void main(String[] args) {
Person monkey = new Person.Builder().head("齐天大圣 - 孙悟空").body("上单 - AD").foot("基石天赋 - 战争雷霆").build();
System.out.println(monkey.toString());
Person mouse = new Person.Builder().head("瘟疫之源 - 图奇").body("下路 - ADC").foot("基石天赋 - 战阵热诚").build();
System.out.println(mouse.toString());
Person diann = new Person.Builder().head("皎月女神 - 戴安娜").body("中单 - AP").foot("基石天赋 - 战阵热诚").build();
System.out.println(diann.toString());
}
}
4.适配器模式
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
// Target 目标接口
public interface Volt5 {
public int getVolt5();
}
// Adaptee 需要被转换的对象
public class Volt220 {
public int getVolt220() {
return 220;
}
}
//Adapter 适配器
public class VoltAdapter implements Volt5 {
Volt220 mVolt220;
public VoltAdapter(Volt220 adaptee) {
mVolt220 = adaptee;
}
public int getVolt220() {
return 220;
}
@Override
public int getVolt5() {
return 5;
}
}
在很多情况下,必须让一个具体类实现某一个接口,但是这个类又用不到接口所规定的所有的方法。通常的处理方法是,这个具体类要实现所有的方法,那些有用的方法要有实现,那些没有用的方法也要有空的、平庸的实现。
这些空的方法是一种浪费,有时也是一种混乱。除非看过这些空方法的代码,程序员可能会以为这些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看过这些方法的源代码或是文档。
拿我们Android开发最熟悉的展示列表数据的三大控件:ListView,GridView,RecyclerView的Adapter来说,它们三个控件需要的是View(target),而我们有的一般是datas(source),所以适配器Adapter就是完成了数据源datas 转化成 ItemView的工作。
类适配器模式:Adapter类,通过继承 source类,实现 target类接口,完成source->target的适配。
象适配器模式:Adapter类,持有source类的实例,实现 target类接口,完成source->target的适配。
接口适配器模式:Adapter类,实现source类的所有接口,在调用时使用部分Adapter接口,完成source->target的适配。
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。
5.代理模式
在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理模式的作用是为其他对象提供一种代理以控制对这个对象的访问。
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
前辈程序员爸爸举了一个潘金莲的栗子,瞬间就弄懂了, 笑cry。。。。
1.先定义一种女人
public interface KindWoman {
//这种女人能做什么事情呢?
public void makeEyesWithMan();//抛媚眼
public void happyWithMan();//和男人那个....
}
2.一种类型嘛,那肯定是接口,定义个潘金莲
public class PanJinLian implements KindWoman{
@Override
public void happyWithMan() {
System.out.println("潘金莲和男人在做那个...");
}
@Override
public void makeEyesWithMan() {
System.out.println("潘金莲抛媚眼...");
}
}
3.再定义个丑陋的王婆
public class WangPo implements KindWoman {
private KindWoman kindWoman;
public WangPo(){
//默认的话是潘金莲的代理
this.kindWoman = new PanJinLian();
}
//她可以是KindWomam的任何一个女人的代理,只要你是这一类型
public WangPo(KindWoman kindWoman){
this.kindWoman = kindWoman;
}
@Override
public void happyWithMan() {
//自己老了,干不了了,但可以叫年轻的代替。
this.kindWoman.happyWithMan();
}
@Override
public void makeEyesWithMan() {
//王婆年纪大了,谁看她抛媚眼啊
this.kindWoman.makeEyesWithMan();
}
}
4.贾氏
public class JiaShi implements KindWoman {
@Override
public void happyWithMan() {
System.out.println("贾氏和男人在做那个...");
}
@Override
public void makeEyesWithMan() {
System.out.println("贾氏抛媚眼...");
}
}
5.西门庆
public class XiMenQiang {
public static void main(String[] args) {
WangPo wangPo;
//把王婆叫出来
wangPo = new WangPo();
//然后西门庆说,我要和潘金莲Happy,然后王婆就安排了西门庆丢筷子哪出戏:
wangPo.makeEyesWithMan();
//看到没有表面是王婆在做,其实爽的是潘金莲
wangPo.happyWithMan();
//西门庆勾引贾氏
JiaShi jiaShi = new JiaShi();
wangPo = new WangPo(jiaShi);
wangPo.makeEyesWithMan();
wangPo.happyWithMan();
}
}
代理模式主要使用了java的多态,干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚得很,同样一个接口呗。
6.观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
抽象被观察者角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。
举个栗子:有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。
1.定义一个抽象被观察者接口
public interface Observerable {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
2.定义一个抽象观察者接口
public interface Observer {
public void update(String message);
}
3.定义被观察者,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。
public class WechatServer implements Observerable {
//注意到这个List集合的泛型参数为Observer接口,设计原则:面向接口编程而不是面向实现编程
private List<Observer> list;
private String message;
public WechatServer() {
list = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer o) {
list.add(o);
}
@Override
public void removeObserver(Observer o) {
if(!list.isEmpty())
list.remove(o);
}
//遍历
@Override
public void notifyObserver() {
for(int i = 0; i < list.size(); i++) {
Observer oserver = list.get(i);
oserver.update(message);
}
}
public void setInfomation(String s) {
this.message = s;
System.out.println("微信服务更新消息: " + s);
//消息更新,通知所有观察者
notifyObserver();
}
}
4.定义具体观察者,微信公众号的具体观察者为用户User
public class User implements Observer {
private String name;
private String message;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
this.message = message;
read();
}
public void read() {
System.out.println(name + " 收到推送消息: " + message);
}
}
5.编写一个测试类
public class Test {
public static void main(String[] args) {
WechatServer server = new WechatServer();
Observer userZhang = new User("ZhangSan");
Observer userLi = new User("LiSi");
Observer userWang = new User("WangWu");
server.registerObserver(userZhang);
server.registerObserver(userLi);
server.registerObserver(userWang);
server.setInfomation("PHP是世界上最好用的语言!");
System.out.println("----------------------------------------------");
server.removeObserver(userZhang);
server.setInfomation("JAVA是世界上最好用的语言!");
}
}
7.策略模式
策略模式管理着一套策略,在不同的情况下用不同的策略解决问题,各种策略之间相互独立,而且可以很灵活的替换。
抽象策略角色: 策略类,通常由一个接口或者抽象类实现。好比例子中的优惠,把这个优惠抽象出来,可以是抽象类可以是接口
具体策略角色:包装了相关的算法和行为。例子中各种会员具体的优惠幅度
环境角色:持有一个策略类的引用,最终给客户端调用。各种会员决定了各种优惠幅度,只一个环境
举个栗子:不同的会员享有不同的折扣。
1.把优惠这个行为抽象出来:
public interface Strategy {
/*
* price:原价*/
public abstract double saleprice(double price);
}
2.低级会员类,继承优惠这个行为:
public class SMemeber implements Strategy {
@Override
public double saleprice(double price) {
return price * (1 - 0.1);
}
}
3.中级会员:
public class MMember implements Strategy {
@Override
public double saleprice(double price) {
return price * (1 - 0.2);
}
}
4.高级会员:
public class BMember implements Strategy {
@Override
public double saleprice(double price) {
return price * (1 - 0.3);
}
}
5.环境类:根据所在环境选择对应的策略
public class Price {
private Strategy strategy;
public Price(Strategy strategy) {
this.strategy = strategy;
}
//根据传入对象的不同,调用不同的方法,优惠策略
public double sale(double bookprice) {
return strategy.saleprice(bookprice);
}
}
6.客户类(测试类):
public class Client {
public static void main(String[] args) {
//创建低级会员
SMemeber s = new SMemeber();
//创建中级会员
MMember m = new MMember();
//创建高级会员
BMember b = new BMember();
//运用低级会员的优惠策略
Price price1 = new Price(s);
//运用中级会员优惠策略
Price price2 = new Price(m);
//运用高级会员优惠策略
Price price3 = new Price(b);
System.out.println("图书原价:100元");
System.out.println("低级会员价:"+price1.sale(100));
System.out.println("中级会员价:"+price2.sale(100));
System.out.println("高级会员价:"+price3.sale(100));
}
}
这他喵的不就是if-else分类吗?想糊弄我?
8.迭代器模式
提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。
抽象迭代器角色:负责定义访问和遍历元素的接口
具体迭代器角色:实现迭代器接口,并要记录遍历中的当前位置
抽象容器角色:负责提供创建具体迭代器角色的接口
具体容器角色:实现创建具体迭代器角色的接口,这个具体迭代器角色与该容器的结构相关
栗子很无味:
1.定义迭代器角色(Iterator)
public interface Iterator {
public boolean hasNext();
public Object next();
}
2.定义具体迭代器角色(Concrete Iterator)
public class ConcreteIterator implements Iterator {
private List list = null;
private int index;
public ConcreteIterator(List list) {
super();
this.list = list;
}
@Override
public boolean hasNext() {
if (index >= list.getSize()) {
return false;
} else {
return true;
}
}
@Override
public Object next() {
Object object = list.get(index);
index++;
return object;
}
}
3.定义容器角色(Aggregate)
public interface List {
public void add(Object obj);
public Object get(int index);
public Iterator iterator();
public int getSize();
}
4.定义具体容器角色(ConcreteAggregate)
public class ConcreteAggregate implements List{
private Object[] list;
private int size=0;
private int index=0;
public ConcreteAggregate(){
index=0;
size=0;
list=new Object[100];
}
@Override
public void add(Object obj) {
list[index++]=obj;
size++;
}
@Override
public Iterator iterator() {
return new ConcreteIterator(this);
}
@Override
public Object get(int index) {
return list[index];
}
@Override
public int getSize() {
return size;
}
}
5.代码测试
public class IteratorTest {
public static void main(String[] args) {
List list=new ConcreteAggregate();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
Iterator it=list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
迭代器模式是与集合共生共死的,一般来说,我们只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List、Set、Map等,这些集合都有自己的迭代器。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。
9.装饰器模式
装饰器设计模式顾名思义就是装饰某个对象,让一个功能单一的对象拥有一些其他的功能,这些功能的添加是动态的。用户可以随意的扩展原有对象的功能。
一方面代替了继承,相对于继承带来的功能扩展,装饰器模式可以理解为动态的扩展,用户需要什么就扩展什么功能,非常灵活,而继承带来的缺点就是不方便修改,是静态的扩展。
由于他的方便也带来了缺点,在装饰的过程中,其实创建了很多的对象占据内存资源,这些对象大多都很相似,排查错误也是有很大困难。
1.这是一个基础电脑抽象接口
public interface AbstractComputer {
public abstract String name();
public abstract double price();
}
2.这是一个基础的不能在基础的基础电脑具体类
public class Computer implements AbstractComputer{
@Override
public String name() {
return "普通电脑";
}
@Override
public double price() {
return 3000;
}
}
3.下面我们做一个硬件抽象类,记住这是一个抽象类,抽象类
public abstract class Device implements AbstractComputer{
public abstract String name();
public abstract double price();
}
4.我们先给它装饰上内存
public class Memory extends Device{
public AbstractComputer computer;
public Memory(AbstractComputer computer) {
this.computer = computer;
}
public String name() {
return computer.name() + "加内存";
}
public double price() {
return computer.price() + 300;
}
}
5.我们再给它装饰上硬盘
public class HardDisk extends Device{
public AbstractComputer computer;
public HardDisk(AbstractComputer computer) {
this.computer = computer;
}
public String name() {
return computer.name() + "加硬盘";
}
public double price() {
return computer.price() + 500;
}
}
6.这将不是一台基础电脑
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
HardDisk h = new HardDisk(computer);
System.out.println(h.name());
System.out.println("价格:"+h.price());
Memory m = new Memory(computer);
System.out.println(m.name());
System.out.println("价格:"+m.price());
HardDisk h1=new HardDisk(m);
System.out.println(h1.name());
System.out.println("价格:"+h1.price());
}
}
装饰器模式很好的体现开闭原则:对于扩展是开放的,对于修改是关闭的。用户可以动态添加功能。在java的io流中也是应用了装饰器模式。
10.模板模式
模板模式定义了一个模板抽象类,这个抽象类中定义了普通方法适用于所有继承的子类,而其中抽象的方法可以由子类自行实现不同的方法体。
就很舒服。举个栗子,顾客去餐馆点餐:
1.这是一个餐馆抽象类
public abstract class Template {
//服务员为顾客选择座位
private void selectSeat() {
System.out.println("选择一个座位");
}
//服务员给顾客菜单
private void passMenu() {
System.out.println("递菜单");
}
//假设顾客只点了A,B,C三个菜
public abstract void selectA();
public abstract void selectB();
public abstract void selectC();
//服务员提交菜单
private void submitMenu() {
System.out.println("提交菜单");
}
//上菜
private void serving() {
System.out.println("上菜");
}
//顾客买单
private void pay() {
System.out.println("顾客买单");
}
protected void service() {
selectSeat();
passMenu();
this.selectA();
this.selectB();
this.selectC();
submitMenu();
serving();
pay();
}
}
2.这是顾客First
public class First extends Template {
@Override
public void selectA() {
System.out.println("地三鲜");
}
@Override
public void selectB() {
System.out.println("大米饭");
}
@Override
public void selectC() {
System.out.println("橙汁");
}
}
3.这是顾客Second
public class Second extends Template {
@Override
public void selectA() {
System.out.println("大米饭");
}
@Override
public void selectB() {
System.out.println("糖醋鲤鱼");
}
@Override
public void selectC() {
System.out.println("地三鲜");
}
}
4.他们点餐
public class Test {
public static void main(String[] args) {
System.out.println("第一位顾客:");
Template first = new First();
first.service();
System.out.println("---------------------");
System.out.println("第二位顾客:");
Template second = new Second();
second.service();
}
}
11.最后
12.最后
13.最后
14.最后
你原本可以做得更好,可惜你没有。