java的设计模式汇总
java的设计模式大体上分为三大类:
创建型模式(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.单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这
个实例。
(1)懒汉式
1 public class Singleton {
23 /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加
载 */
4 private static Singleton instance = null;
5
6 /* 私有构造方法,防止被实例化 */
7 private Singleton() {
8 }
9
10 /* 1:懒汉式,静态工程方法,创建实例 */
11 public static Singleton getInstance() {
12 if (instance == null) {
13 instance = new Singleton();
14 }
15 return instance;
16 }
17 }
(2)饿汉式
1 public class Singleton {
2
3 /* 持有私有静态实例,防止被引用 */
4 private static Singleton instance = new Singleton();
5
6 /* 私有构造方法,防止被实例化 */
7 private Singleton() {
8 }
9
10 /* 1:懒汉式,静态工程方法,创建实例 */
11 public static Singleton getInstance() {
12 return instance;
13 }
14 }
使用场景:
1.要求生成唯一序列号的环境;2.在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数
器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并
确保是线程安全的;
3.创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
4.需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式
(当然,也可以直接声明为static的方式)。
2.工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂
方法使一个类的实例化延迟到其子类。
接口
1 public interface Fruit {
2 public void print();
3 }
4 2个实现类
5
6 public class Apple implements Fruit{
7
8 @Override
9 public void print() {
10 System.out.println(\”我是一个苹果\”);
11 }
12
13 }
14 public class Orange implements Fruit{
15
16 @Override
17 public void print() {
18 System.out.println(\”我是一个橘子\”);
19 }
20
21 }
工厂类1 public class FruitFactory {
2 public Fruit produce(String type){
3 if(type.equals(\”apple\”)){
4 return new Apple();
5 }else if(type.equals(\”orange\”)){
6 return new Orange();
7 }else{
8 System.out.println(\”请输入正确的类型!\”);
9 return null;
10 }
11 }
12 }
使用场景:jdbc连接数据库,硬件访问,降低对象的产生和销毁
3.抽象工厂模式:为创建一组相关或相互依赖的对象提供一个接口,而且无须指
定它们的具体类。
相对于工厂模式,我们可以新增产品类(只需要实现产品接口),只需要同时新
增一个工厂类,客户端就可以轻松调用新产品的代码。
1 interface food{}
2 class A implements food{}
3 class B implements food{}
4 interface produce{ food get();}
5 class FactoryForA implements produce{
6 @Override
7 public food get() {
8 return new A();
9 }
10 }
11 class FactoryForB implements produce{
12 @Override
13 public food get() {
14 return new B();
15 }
16 }17 public class AbstractFactory {
18 public void ClientCode(String name){
19 food x= new FactoryForA().get();
20 x = new FactoryForB().get();
21 }
22 }
使用场景:一个对象族(或是一组没有任何关系的对象)都有相同的约束。
涉及不同操作系统的时候,都可以考虑使用抽象工厂模式
4.建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可
以创建不同的表示。
1 public class Build {
2 static class Student{
3 String name = null ;
4 int number = ‐1 ;
5 String sex = null ;
6 public Student(Builder builder)
7 {
8 this.name=builder.name;
9 this.number=builder.number;
10 this.sex=builder.sex;
11 }
12 static class Builder{
13 String name = null ;
14 int number = ‐1 ;
15 String sex = null ;
16 public Builder setName(String name){
17 this.name=name;
18 return this;
19 }
20 public Builder setNumber(int number){
21 this.number=number;
22 return this;
23 }
24 public Builder setSex(String sex){25 this.sex=sex;
26 return this;
27 }
28 public Student build(){
29 return new Student(this);
30 }
31
32 }
33 }
34 public static void main(String[] args) {
35 Student A=new Student.Builder().setName(\”张
三\”).setNumber(1).build();
36 Student B=new Student.Builder().setSex(\”男\”).setName(\”李
四\”).build();
37 System.out.println(A.name+\” \”+A.number+\” \”+A.sex);
38 System.out.println(B.name+\” \”+B.number+\” \”+B.sex);
39 }
40 }
使用场景:
1. 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模
式。
2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同
时,则可以使用该模式。
3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候
使用建造者模式非常合适。
5.原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的
对象。
1 public class Prototype implements Cloneable{
2 private String name;
3 public String getName() {
4 return name;
5 }
6 public void setName(String name) {
7 this.name = name;8 }
9 @Override
10 protected Object clone() {
11 try {
12 return super.clone();
13 } catch (CloneNotSupportedException e) {
14 e.printStackTrace();
15 }finally {
16 return null;
17 }
18 }
19 public static void main ( String[] args){
20 Prototype pro = new Prototype();
21 Prototype pro1 = (Prototype)pro.clone();
22 }
23 }
原型模式实际上就是实现Cloneable接口,重写clone()方法。
使用原型模式的优点:
1.性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是
要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
2.逃避构造函数的约束
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(参见
13.4节)。
使用场景:
1.资源优化场景
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
2.性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模
式。
3. 一个对象多个修改者的场景一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可
以考虑使用原型模式拷贝多个对象供调用者使用。
浅拷贝和深拷贝:
浅拷贝:Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用
对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,
其他的原始类型比如int、long、char、string(当做是原始类型)等都会被拷
贝。
注意: 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一
是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是
一个原始类型或不可变对象。
深拷贝:对私有的类变量进行独立的拷贝
如:this.arrayList = (ArrayList)this.arrayList.clone();
6.适配器模式:将一个类的接口变换成客户端所期待的另一种接口,从而使原本
因接口不匹配而无法在一起工作的两个类能够在一起工作。
主要可分为3种:
1.类适配:创建新类,继承源类,并实现新接口,例如
1 class adapter extends oldClass implements newFunc{}
2.对象适配:创建新类持源类的实例,并实现新接口,例如
1 class adapter implements newFunc { private oldClass oldInstance
;}
3.接口适配:创建新的抽象类实现旧接口方法。例如
1 abstract class adapter implements oldClassFunc { void
newFunc();}
使用场景:
你有动机修改一个已经投产中的接口时,适配器模式可能是最适合你的模式。比
如系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统的接
口,怎么办?使用适配器模式,这也是我们例子中提到的。
7.装饰器模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰
器模式相比生成子类更为灵活1 interface Source{ void method();}
2 public class Decorator implements Source{
3 private Source source ;
4 public void decotate1(){
5 System.out.println(\”decorate\”);
6 }
7 @Override
8 public void method() {
9 decotate1();
10 source.method();
11 }
12 }
使用场景:
1. 需要扩展一个类的功能,或给一个类增加附加功能。
2. 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
3. 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。
8.代理模式:为其他对象提供一种代理以控制对这个对象的访问。
1 interface Source{
2 void method();
3 }
4 class OldClass implements Source{
5 @Override
6 public void method() {
7 }
8 }
9 class Proxy implements Source{
10 private Source source = new OldClass();
11 void doSomething(){}
12 @Override
13 public void method() {
14 new Class1().Func1();
15 source.method();
16 new Class2().Func2();
17 doSomething();18 }
19 }
9.中介者模式:用一个中介对象封装一系列的对象交互,中介者使各对象不需要
显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
1 public abstract class Mediator {
2 //定义同事类
3 protected ConcreteColleague1 c1;
4 protected ConcreteColleague2 c2;
5 //通过getter/setter方法把同事类注入进来
6 public ConcreteColleague1 getC1() {
7 return c1;
8 }
9 public void setC1(ConcreteColleague1 c1) {
10 this.c1 = c1;
11 }
12 public ConcreteColleague2 getC2() {
13 return c2;
14 }
15 public void setC2(ConcreteColleague2 c2) {
16 this.c2 = c2;
17 }
18 //中介者模式的业务逻辑
19 public abstract void doSomething1();
20 public abstract void doSomething2();
21 }
使用场景:
中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中
出现了蜘蛛网状结构,即每个类都与其他的类有直接的联系。
10.命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端
参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
Receiver接受者角色:该角色就是干活的角色,命令传递到这里是应该被执行的Command命令角色:需要执行的所有命令都在这里声明
Invoker调用者角色:接收到命令,并执行命令
1 //通用Receiver类
2 public abstract class Receiver {
3 public abstract void doSomething();
4 }
5 //具体Receiver类
6 public class ConcreteReciver1 extends Receiver{
7 //每个接收者都必须处理一定的业务逻辑
8 public void doSomething(){ }
9 }
10 public class ConcreteReciver2 extends Receiver{
11 //每个接收者都必须处理一定的业务逻辑
12 public void doSomething(){ }
13 }
14 //抽象Command类
15 public abstract class Command {
16 public abstract void execute();
17 }
18 //具体的Command类
19 public class ConcreteCommand1 extends Command {
20 //对哪个Receiver类进行命令处理
21 private Receiver receiver;
22 //构造函数传递接收者
23 public ConcreteCommand1(Receiver _receiver){
24 this.receiver = _receiver;
25 }
26 //必须实现一个命令
27 public void execute() {
28 //业务处理
29 this.receiver.doSomething();
30 }
31 }
32 public class ConcreteCommand2 extends Command {
33 //哪个Receiver类进行命令处理34 private Receiver receiver;
35 //构造函数传递接收者
36 public ConcreteCommand2(Receiver _receiver){
37 this.receiver = _receiver;
38 }
39 //必须实现一个命令
40 public void execute() {
41 //业务处理
42 this.receiver.doSomething();
43 }
44 }
45 //调用者Invoker类
46 public class Invoker {
47 private Command command;
48
49 public void setCommand(Command _command){
50 this.command = _command;
51 }
52
53 public void action() {
54 this.command.execute();
55 }
56 }
57 //场景类
58 public class Client {
59 public static void main(String[] args){
60 Invoker invoker = new Invoker();
61 Receiver receiver = new ConcreteReceiver1();
62
63 Command command = new ConcreteCommand1(receiver);
64 invoker.setCommand(command);
65 invoker.action();
66 }
67 }
使用场景:认为是命令的地方就可以采用命令模式,例如,在GUI开发中,一个按钮的点击
是一个命令,可以采用命令模式;模拟DOS命令的时候,当然也要采用命令模
式;触发-反馈机制的处理等。
11.责任链模式:使多个对象都有机会处理请求,从而避免了请求的发送者和接
受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到
有对象处理它为止。
1 public abstract class Handler {
2 private Handler nextHandler;
3 //每个处理者都必须对请求做出处理
4 public final Response handleMessage(Request request){
5 Response response = null;
6 //判断是否是自己的处理级别
7 if(this.getHandlerLevel().equals(request.getRequest
Level())){
8 response = this.echo(request);
9 }else{ //不属于自己的处理级别
10 //判断是否有下一个处理者
11 if(this.nextHandler != null){
12 response = this.nextHandler.handleM
essage(request);
13 }else{
14 //没有适当的处理者,业务自行处理
15 }
16 }
17 return response;
18 }
19 //设置下一个处理者是谁
20 public void setNext(Handler _handler){
21 this.nextHandler = _handler;
22 }
23 //每个处理者都有一个处理级别
24 protected abstract Level getHandlerLevel();
25 //每个处理者都必须实现处理任务
26 protected abstract Response echo(Request request);
27 }12.策略模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以互
换。
使用场景:
1. 多个类只有在算法或行为上稍有不同的场景。
2. 算法需要自由切换的场景。
3. 需要屏蔽算法规则的场景。
13.迭代器模式:它提供一种方法访问一个容器对象中各个元素,而又不需暴露
该对象的内部细节。
迭代器模式已经被淘汰,java中已经把迭代器运用到各个聚集类(collection)
中了,使用java自带的迭代器就已经满足我们的需求了。
14.组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用
户对单个对象和组合对象的使用具有一致性。
1 public class Composite extends Component {
2 //构件容器
3 private ArrayList componentArrayList = new ArrayList();
4 //增加一个叶子构件或树枝构件
5 public void add(Component component){
6 this.componentArrayList.add(component);
7 }
8 //删除一个叶子构件或树枝构件
9 public void remove(Component component){
10 this.componentArrayList.remove(component);
11 }
12 //获得分支下的所有叶子构件和树枝构件
13 public ArrayList getChildren(){
14 return this.componentArrayList;
15 }
16 }
使用场景:
1. 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
2. 从一个整体中能够独立出部分模块或功能的场景。15.观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状
态,则所有依赖于它的对象都会得到通知并被自动更新。
1 public abstract class Subject {
2 //定义一个观察者数组
3 private Vector obsVector = new Vector();
4 //增加一个观察者
5 public void addObserver(Observer o){
6 this.obsVector.add(o);
7 }
8 //删除一个观察者
9 public void delObserver(Observer o){
10 this.obsVector.remove(o);
11 }
12 //通知所有观察者
13 public void notifyObservers(){
14 for(Observer o:this.obsVector){
15 o.update();
16 }
17 }
18 }
使用场景:
1. 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关
系。
2. 事件多级触发场景。
3. 跨系统的消息交换场景,如消息队列的处理机制
16.门面模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象
进行。门面模式提供一个高层次的接口,使得子系统更易于使用。
1 public class Facade {
2 private subSystem1 subSystem1 = new subSystem1();
3 private subSystem2 subSystem2 = new subSystem2();
4 private subSystem3 subSystem3 = new subSystem3();
5
6 public void startSystem(){
7 subSystem1.start();8 subSystem2.start();
9 subSystem3.start();
10 }
11
12 public void stopSystem(){
13 subSystem1.stop();
14 subSystem2.stop();
15 subSystem3.stop();
16 }
17 }
使用场景:
1. 为一个复杂的模块或子系统提供一个供外界访问的接口
2. 子系统相对独立——外界对子系统的访问只要黑箱操作即可
3. 预防低水平人员带来的风险扩散
17.备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
1 public class Originator {
2 private String state;
3 /**
4 * 工厂方法,返回一个新的备忘录对象
5 */
6 public Memento createMemento(){
7 return new Memento(state);
8 }
9 /**
10 * 将发起人恢复到备忘录对象所记载的状态
11 */
12 public void restoreMemento(Memento memento){
13 this.state = memento.getState();
14 }
15 public String getState() {
16 return state;
17 }
18 public void setState(String state) {19 this.state = state;
20 System.out.println(\”当前状态:\” + this.state);
21 }
22 }
使用场景:
1. 需要保存和恢复数据的相关状态场景。
2. 提供一个可回滚(rollback)的操作。
3. 需要监控的副本场景中。
4. 数据库连接的事务管理就是用的备忘录模式。
18.访问者模式:封装一些作用于某种数据结构中的各元素的操作,它可以在不
改变数据结构的前提下定义作用于这些元素的新的操作。
使用场景:
1. 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施
一些依赖于其具体类的操作,也就说是用迭代器模式已经不能胜任的情景。
2. 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免
让这些操作“污染”这些对象的类。
19.状态模式:当一个对象内在状态改变时允许其改变行为,这个对象看起来像
改变了其类。
使用场景:
1. 行为随状态改变而改变的场景
这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的
行为结果也会不同,在这种情况下需要考虑使用状态模式。
2. 条件、分支判断语句的替代者
20.解释器模式:给定一门语言,定义它的文法的一种表示,并定义一个解释
器,该解释器使用该表示来解释语言中的句子。
使用场景:
1. 重复发生的问题可以使用解释器模式
2. 一个简单语法需要解释的场景21.享元模式:使用共享对象的方法,用来尽可能减少内存使用量以及分享资
讯。
1 abstract class flywei{ }
2 public class Flyweight extends flywei{
3 Object obj ;
4 public Flyweight(Object obj){
5 this.obj = obj;
6 }
7 }
8 class FlyweightFactory{
9 private HashMap data;
10 public FlyweightFactory(){ data = new HashMap<>();}
11 public Flyweight getFlyweight(Object object){
12 if ( data.containsKey(object)){
13 return data.get(object);
14 }else {
15 Flyweight flyweight = new Flyweight(object);
16 data.put(object,flyweight);
17 return flyweight;
18 }
19 }
20 }
使用场景:
1. 系统中存在大量的相似对象。
2. 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是
说对象没有特定身份。
3. 需要缓冲池的场景。
22.桥梁模式:将抽象和实现解耦,使得两者可以独立地变化。
Circle类将DrwaApi与Shape类进行了桥接,
1 interface DrawAPI {
2 public void drawCircle(int radius, int x, int y);
3 }
4 class RedCircle implements DrawAPI {5 @Override
6 public void drawCircle(int radius, int x, int y) {
7 System.out.println(\”Drawing Circle[ color: red, radius:
\”
8 + radius +\”, x: \” +x+\”, \”+ y +\”]\”);
9 }
10 }
11 class GreenCircle implements DrawAPI {
12 @Override
13 public void drawCircle(int radius, int x, int y) {
14 System.out.println(\”Drawing Circle[ color: green, radiu
s: \”
15 + radius +\”, x: \” +x+\”, \”+ y +\”]\”);
16 }
17 }
18 abstract class Shape {
19 protected DrawAPI drawAPI;
20 protected Shape(DrawAPI drawAPI){
21 this.drawAPI = drawAPI;
22 }
23 public abstract void draw();
24 }
25 class Circle extends Shape {
26 private int x, y, radius;
27 public Circle(int x, int y, int radius, DrawAPI drawAPI) {
28 super(drawAPI);
29 this.x = x;
30 this.y = y;
31 this.radius = radius;
32 }
33 public void draw() {
34 drawAPI.drawCircle(radius,x,y);
35 }
36 }
37 //客户端使用代码38 Shape redCircle = new Circle(100,100, 10, new RedCircle());
39 Shape greenCircle = new Circle(100,100, 10, new GreenCircle());
40 redCircle.draw();
41 greenCircle.draw();
使用场景:
1. 不希望或不适用使用继承的场景
2. 接口或抽象类不稳定的场景
3. 重用性要求较高的场景
23.模板方法模式:定义一个操作中的算法的框架,而将一些步骤延迟到子类
中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
使用场景:
1. 多个子类有公有的方法,并且逻辑基本相同时。
2. 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能
则由各个子类实现。
3. 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类
中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。
对java设计模式的理解、总结
1、在java中,什么时候用重载,什么使用用重写?
重载(Overloading)和重写(Overriding)都可以对业务功能的增强。
重载: 在同一个类中,方法名一样,但是参数不一样(即参数的数量或参数类型不能完全相同)
重写: 在父子类中,子类重写了父类中的方法。需要具有相同的方法名、参数、返回值。
重载 就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。
调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
重载中返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
重写 中访问修饰符的限制一定要大于被重写方法的访问修饰(public>protected>default>private)
被final修饰和private修饰的方法不能被重写
代码中应用: 重载:之前写的登录,在系统内是loginname和password两个参数才能登录,但是因为业务需要跟第三方对接接口的时候,可以从他们系统直接登录到我们系统,但是没有loginname和password,只有根据双方系统中都有的一个字段身份证来认证,我处理方法:根据第三方传来的身份证号生成token并且返回,他们带着token再次来请求。所以我要写一个login()的重载方法。跟之前的参数不一致,只需一个参数token就可以。 重写: 平时和同事写一个类似的功能,所需参数一样,返回值也一样。就仅仅是业务处理可能不一样,我们就会在父类中写声明一个方法,然后我俩同时重写这个方法,然后各自写自己的业务就行。
2、举例一个你更倾向于抽象类,而不是接口的代码设计场景
抽象类和接口都是对事物的一种抽象, 接口是对动作的一种抽象,而实现类是对根源的抽象。
接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
抽象方法要被实现,所以不能是静态的,也不能是私有的。
接口可继承接口,并可多继承接口,但类只能单根继承。
当业务中,有一些功能是一致的,大多时候都不会被重写的,这时候可以使用抽象类定义一个具体的实现方法,这样就不用子类一一实现了。例如在spring中的JDBCtemplate中就使用了抽象类作为模板设计模式基础。
3、在java中,为什么不允许静态方法中访问非静态变量?
静态方法只能调用静态方法或者变量,非静态方法只能调用非静态方法或者变量。
一个类的静态变量和静态方法是属于这个类本身的,在类被加载的时候就会分配内存(方法区),可以直接通过类名点的方式来调用, 而非静态方法和变量是属于对象的,也只有在外部使用这个类的对象时,比如 new 这个对象时, 非静态变量和方法才会被加载到堆内存中。
所以说,静态变量和方法是优先加载的,而且只加载一次,非静态变量和方法是外部使用的时候才被加载。 所以当静态变量和方法加载后, 非静态变量和方法可能没有被加载,所以不能访问。
在开发中我们会使用很多工具类,在写工具类的时候我们一般都使用的是静态方法,因为不用每次调用的时候都要去new 出来,不用频繁的读写内存, 而是从内存中直接取出来。
4、列举你说了解的软件分层场景,软件分层有哪些好处?
举例: 就像我们在开发中会将代码分为3层,
表现层(controller) 与前端交互数据。
业务层(service) 处理业务
持久层(dao) 与数据库交互,一般写sql
这样分层可以使编写代码的时候各行其志,我们在各自的分层中写对应的代码。
经典的MVC架构:
模型(model)、视图(view)、控制器(controller)。
模型: 关注数据的处理,主要负责业务代码和数据操作。
视图: 页面。 显示数据用的,例如 html 、jsp等。
控制器: 用于 视图和模型 之间的协调
好处: 1、让代码看起来更加清晰。可维护性高。
2、模型和视图之间的耦合性低。
5、请列举符合开闭原则的设计模式。
先说一下开闭原则: 一个软件实体应当对外扩展开放,对修改关闭。 即软件实体应尽量在不修改原有的代码情况下进行扩展。
1、适配器模式: 在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。适配器类继承了目标抽象类,而适配器又有适配者的引用。
2、装饰者设计模式: 装饰者设计模式的本意就是不改变一个对象本身功能的基础上给对象增加额外的新行为。 装饰模式是一种用于替代继承的技术,在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能
3、工厂方法模式:工厂方法模式引用了 抽象工厂角色。 这样在我们添加具体的产品工厂的时候,只需要继承或实现抽象工厂就行,不用修改原有代码。
4、观察者模式: 定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅。 当我们需要增加一个具体的观察者时候,不需要修改原有的代码, 而是只需要继承之前定义的观察者接口即可。
5、代理模式: 代理模式分为JDK动态代理和cgLib动态代理, 代理模式是给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。我们要增强某个方法的时候,只需要在代理类都完成就行,不用在原有代码上做修改。
6、策略模式: 实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用策略模式来实现灵活地选择解决途径。 我们在使用策略模式的时候可以在不修改代码的情况下 增加新的策略方式。
7、模板方法模式: 模板方法模式是一种基于继承的代码复用技术。 在父类中提供了一个定义算法框架的模板方法,还提供了一系列抽象方法、具体方法和钩子方法。 我们可以通过添加不同的子类,来完善我们的需求。但是这样有个不好的地方,就是会导致我们的子类越来越多。
6.总结设计模式中创建型、结构型、行为型之间的根本区别?
创建型
创建型模式与对象的创建有关。
结构型
结构型模式处理类或对象的组合,它采用继承机制来组合接口或实现(类结构型模式),或者通过组合一些对象,从而实现新的功能(对象结构型模式)
行为型
行为型模式对类或对象怎样交互和怎样分配职责进行描述
模式创建型模式结构型模式行为型模式概念创建型模式:就是创建对象的模式,抽象了实例化的过程。也可以理解为将创建的对象的过程进行了封装,作为使用者仅仅知道怎么样去使用对象,而不会关心创建对象过程的逻辑结构型模式是为了解决怎么样组装现有的类,设计它们的交互方式,从而达到实现功能的目的。行为型模式是对类或者对象进行交互的描述,包含的设计模式1、简单工厂模式 2、工厂方法模式 3、抽象工厂模式 4、单例模式 5、原型模式 6、建造者模式1、适配器模式 2、桥接模式 3.组合模式 4、装饰模式 5、外观模式 6、享元模式 7、代理模式1、责任链模式 2、命令模式 3、解释器模式 4、迭代器模式 5、中介者模式 6、备忘录模式 7、观察者模式 8、状态模式 9.策略模式 10、模板方法模式 11访问者模式
7、在构造方法中抛出异常是阻止反射破坏单例的唯一方法嘛? 为什么?
不是。在构造方法中抛出异常可以阻止反射破坏单例。因为反射可以获取到私有的构造方法,这样就会根据构造来创建对象,破坏单例,所以在构造方法中抛出异常可以阻止反射破坏单例。如代码:
当然还有其他的方式可以阻止反射破坏单例。 枚举(注册式单例)可以。枚举是jdk1.5之后出现的。
所有的枚举编译后都继承了Enum(抽象类)
在JDK源码中我们可以看到反射的 Constructor的 newInstance()的代码反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。
8、双重检查锁为何要做两次非空检测?
第一个判断主要是为了做性能优化,第二个判断是做逻辑控制的。
9、原型模式能够给你解决的最大的麻烦是什么?
在之前学设计模式的时候,我们都知道原型模式是一种创建型模式。通过copy这些原型来创建新的对象。
原型模式原理: 是通过克隆的方式创建一个全新的对象,全新的对象在内存中有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。
说到克隆又分为两种: 浅克隆和深克隆。
浅克隆
在浅克隆中如果原型对象中的成员变量是基本数据类型的话,将直接复制一份给新的克隆对象。 但是如果成员变量是引用数据类型的话, 是会将引用对象在内存中的地址复制一份给新的克隆对象。Object中clone()方法就是浅拷贝。 注意: 需要注意的是能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常
深克隆
在深克隆中无论原型对象中的成员变量是基本数据类型还是引用数据类型。都将复制一份给新的克隆对象。在代码中我们可以通过实现序列来完成深克隆(Serializable),—> 通过序列化实现的拷贝不仅可以复制对象本身,而且可以复制其引用的成员对象,因此通过序列化将对象写到一个流中,再从流里将其读出来,可以实现深克隆。
在代码中使用
在写代码中,碰到过要将一个对象传递给另一个对象, 这两个对象是一个类的实例化。 之前我是通过先new出来一个新对象然后通过get()、set()。将旧对象的值复制给新的对象,如果用原型模式的话,可以省去很多set()、get()。使用Beanutils 作为浅拷贝。使用Serializable序列化实现深拷贝。
10、简单介绍一下工厂模式,并描述他们各自的优劣以及使用场景?
工厂模式(创建型模式)分为3种:简单工厂模式、工厂方法模式、抽象工厂模式。
简单工厂模式:
优点:1、工厂类中包含了必要的判断逻辑,可以决定在什么时候创建哪一个产品类的对象。而客户端可以不用管对象(实例)的创建。实现了对象的创建与使用的分离。2、客户端不需要知道要使用的实例的具体类型,只需要知道具体的产品类对应的参数即可。
缺点:1、工厂类中有很多 创建实例的逻辑代码,一旦不能正常工作,整个系统都会受到影响。2、使用简单工厂模式肯定会出现很多工厂类,会增加系统的复杂度。
适用场景:工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂
工厂方法模式:
优点:1、基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。2、使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点:1、在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度。
使用场景:客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
抽象工厂模式:
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。
优点:1、但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这个就是抽象工厂模式的思想。
11、如何使用最精简的语句描述产品等级结构和产品族?
产品等级结构: 产品等级结构即产品的继承结构。 如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
产品族:在抽象工厂中, 产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品。如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。
12、实现代理模式的底层逻辑是什么?
代理模式概念
给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。 属于结构型模式
代理模式:包含了 代理类、真实类。代理对象需要含有真实类的引用, 底层逻辑其实就是字节码重组。
静态代理
写一个增强类实现 被增强类实现的所有接口,需要把被增强类的引用获取到
JDK动态代理
前提条件:
①必须知道要增强的接口
②必须用Proxy.newProxyInstance创建代理对象
③动态代理只能增强接口中定义的方法
动态代理实现原理: 用动态代理其实是重新生成了一个代理类, 这个代理类和原类 实现了同样的接口。
CGLIB动态代理
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)
CGLib 动态代理执行代理方法效率之所以比 JDK 的高是因为 Cglib 采用了 FastClass 机制,它的原理简单来说就是:为代理类和被代理类各生成一个 Class,这个 Class 会为代理类或被代理类的方法分配一个 index(int 类)。这个 index 当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK动态代理通过反射调用高。
13、为什么JDK Proxy 要求目标类实现接口,而CGLIB对目标类无任何要求?
JDK Proxy
在JDK Proxy中生成的代理类需要继承Proxy。获取(继承)含有InvocationHandler的构造方法,通过Proxy的InvocationHandler可以调用到invoke(), 这样才可以增强方法。在需要继承proxy类获得有关方法和InvocationHandler构造方法传参的同时,java不能同时继承两个类,我们需要和想要代理的类建立联系,只能实现一个接口
CGLIB
CGLIb的实现原理是 生成一个代理类,且代理类继承了被代理类(原类)。所以只要被代理类(原类)能被继承,也就是没有被final修饰。就可以生成代理类。
14、策略模式的本质是什么? 多个策略可以组合使用嘛?如果可以该如何设计?
策略模式的来源:
在开发中实现一个功能有很多路径,每个路径对应着有一种算法。这时候可以使用一种设计模式来实现灵活的选择解决路径,也可以方便的添加新的路径。这种模式称之为策略模式。
策略模式的的本质(目的):
策略模式主要是将算法的定义与算法的调用(使用)分开。 将算法专门定义在专门的策略类中,每一个策略类中封装了一种算法实现。 使用的时候针对 抽象策略类编程, 符合“依赖倒转原则”。在出现新的算法时,只需要增加一个新的实现了抽象策略类的具体策略类即可。 策略模式是一种 行为型模式。
多个策略可以组合使用。如果是让我来设计,1、我首先考虑用到装饰者模式,因为所有策略类都可以去继承与抽象的策略类, 假如 策略A和策略B。 策略A想要使用到策略B的算法, 那么只需要拿到策略B的引用,那么就可以取组合策略B中的算法。 2、代理模式也可以搞定。3、适配器模式可以。 4、委派模式,可以通过委派类来对具体的策略类进行组合使用。
15、代理模式和装饰器模式的区别和使用场景?
首先代理模式和装饰器模式都属于结构型模式。
区别: 代理模式主要分为: 静态代理和动态代理两种, 其中静态代理和装饰者模式实现的机制是一样的。 但是它们的目的(职责)是不一样的。装饰模式是 以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案, 它主要目的是 为所装饰的对象增强功能,装饰模式重在装饰,添加新的行为,但是不论添加什么都是方法所知道的,经过安检才装饰上的; 而代理模式是为原对象生成了一个代理类。并且有代理对象控制了原对象的引用。 也就是说原对象的一切操作,交给了代理类。代理类全权负责了, 代理类控制了原对象,并不提供对象本身的增强功能。 代理模式重在控制。可以在方法调用前,调用后增加行为,甚至彻底改变方法的行为
使用场景: 装饰者模式在源码中的使用: 在 JDK 中体现最明显的类就是 IO 相关的类,如BufferedReader、InputStream、OutputStream。一下情况可以使用装饰者模式: (1) 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。(2)当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类已定义为不能被继承(如Java语言中的final类) 代理模式: 1、当客户端对象需要访问远程主机中的对象时可以使用 远程代理。2、当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理。3、 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理。
平时怎么使用?
如果你写了一个方法,后续考虑要给这个方法添加一些功能,可以选择装饰模式。
如果你要写一堆方法,后续考虑统一给这些方法添加功能(比如日志记录,资源回收),可以选择代理模式。
16、聊聊观察者模式的设计思想。
观察者模式用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
观察者模式也可以被称为发布-订阅模式。 一个对象状态发生改变,其相关依赖的对象都可以得到通知,并且自动更新。
17、工作中了除了打架熟悉的MQ,还有哪些场景可以使用观察者模式?
java中的Observable类(java.util)和Observer 接口 实现了观察者模式。
Observer接口
当观察目标的状态发生变化时,该方法将会被调用,在Observer的子类中将实现update()方法,即具体观察者可以根据需要具有不同的更新行为。当调用观察目标类Observable的notifyObservers()方法时,将执行观察者类中的update()方法。
Observable类
java.util.Observable类充当观察目标类,在Observable中定义了一个向量Vector来存储观察者对象,
方法名方法描述Observable()构造方法,实例化Vector向量。addObserver(Observer o)用于注册新的观察者对象到向量中。deleteObserver (Observer o)用于删除向量中的某一个观察者对象notifyObservers()和notifyObservers(Object arg)通知方法,用于在方法内部循环调用向量中每一个观察者的update()方法。deleteObservers()用于清空向量,即删除向量中所有观察者对象。setChanged()该方法被调用后会设置一个boolean类型的内部标记变量changed的值为true,表示观察目标对象的状态发生了变化。clearChanged()用于将changed变量的值设为false,表示对象状态不再发生改变或者已经通知了所有的观察者对象,调用了它们的update()方法。hasChanged()用于测试对象状态是否改变。countObservers()用于返回向量中观察者的数量。
MVC(Model-View-Controller)架构中也应用了观察者模式,MVC是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。其中模型可对应于观察者模式中的观察目标,而视图对应于观察者,控制器可充当两者之间的中介者。当模型层的数据发生改变时,视图层将自动改变其显示内容
18、什么场景下才应该使用委派模式?
先说一下委派模式的概念。 委派模式是用来负责任务的调度和分配任务的。 还有就是委派模式不属于23种设计模式。
委派模式有点像代理模式,又有点像策略模式。
举个例子:公司老板给项目经理下达任务,将任务全权交给项目经理,由项目经理根据一定的策略将任务分配给小组成员。 这其中老板给项目经理下达任务,相当于把工作委派给了项目经理。由项目经理全权负责, 我们也可以说项目经理其实就是老板的代理。(但是代理模式注重的是过程,二委派模式注重的是结果。)项目经理在给小组成员分配任务的时候,根据不同的人擅长的技术不一样来分配任务。 这时候又特别像策略模式。
委派模式的核心就是分发、调度、派遣。也可以这么说 委派模式就是静态代理和策略模式一种特殊的组合
springmvc源码中使用了委派模式。DispatcherServlet
19、为什么双亲委派一般使用继承来实现?
我理解是使用继承实现是根据加载的顺序有关系。 因为大家都知道双亲委派机制: 如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。 因为它每次加载都是先找父加载器的。 如果不使用继承的话,比如使用接口实现,那么可能会有很多接口。我们很难区分是哪个父接口是他的父加载器。 使用继承,利用单继承特性,我们就能直接找到父加载器,然后进行接下来的操作。
类加载器
- 启动类加载器(Bootstrap ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<JAVA_HOME>\\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。
- 其他类加载器:由Java语言实现,继承自抽象类ClassLoader。如:扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\\lib\\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
20、软件设计阶段可能应用适配器模式吗? 如果用,在什么场景下才应用?
适配器模式(Adapter模式)便是结构型中的其中一个模式,它的作用是:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使原本由于接口不兼容而不能在一起工作的类可以一起工作。 适配器模式应用场景 (1)想要使用一个已经存在的类,但是它却不符合现有的接口规范,导致无法直接去访问,这时创建一个适配器就能间接去访问这个类中的方法。 (2)我们有一个类,想将其设计为可重用的类(可被多处访问),我们可以创建适配器来将这个类来适配其他没有提供合适接口的类。 以上两个场景其实就是从两个角度来描述一类问题,那就是要访问的方法不在合适的接口里,一个从接口出发(被访问),一个从访问出发(主动访问)。 一般在软件设计阶段不会考虑适配器模式,而是在随着软件维护,由于不同产品不同厂家造成功能相同而接口不同的情况下的解决方案。
而在设计阶段中,如果想要使用接口中的某个或某些方法,但是接口中有太多方法,我们要使用时必须实现接口并实现其中的所有方法,可以使用抽象类来实现接口,并不对方法进行实现(仅置空),然后我们再继承这个抽象类来通过重写想用的方法的方式来实现。这个抽象类就是适配器。这种场景下会使用到适配器模式。
21、模板方法模式如何与其他设计模式结合?
先说一下模板模式吧。 模板模式是一种基于继承的代码复用技术。是一种类行为型模式。
一个模板方法是定义在抽象类中的、把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。由于模板方法是具体方法,因此模板方法模式中的抽象层只能是抽象类,而不是接口。
模板方法模式中,在父类中提供了一个定义算法框架的模板方法,还提供了一系列抽象方法、具体方法和钩子方法,其中钩子方法的引入使得子类可以控制父类的行为
1、模板方法模式可以和策略模式结合起来使用,具体的模板方法类,实现了抽象类之后,可以引入策略模式,根据不同的业务需求,选择最好的算法来解决问题
2、模板方法模式可以和装饰者模式结合, 因为两种模式都是基于继承的,所以在模板方法中可以使用装饰者模式来增加某个具体的模板方法。
22、责任链模式能解决什么问题?如何解决?
责任链模式(Chain of Responsibility)
定义: 避免发送者和接受者耦合在一起, 让多个对象都有可能接收请求,让这些对象连接成一条链, 并且让这条链传递请求, 知道有对象处理请求为止。 是一种行为型模式。
职责链模式并不创建职责链,职责链的创建工作必须由系统的其他部分来完成,一般是在使用该职责链的客户端中创建职责链。
责任链模式中,一个处理者只有两种选择: 1、承担全部责任, 2、将责任推给下家。
使用场景: 在Web应用开发中创建一个过滤器(Filter)链来对请求数据进行过滤,在工作流系统中实现公文的分级审批等等,使用职责链模式可以较好地解决此类问题。 如果我们遇到这种需求: 有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的。 我们就可以使用责任链模式。
责任链解决问题的优势:
1、请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。2、在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。3、 在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的。
Java设计模式:23种设计模式全面解析(超级详细)
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。 1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。
这 23 种设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性,以及类的关联关系和组合关系的充分理解。当然,软件设计模式只是一个引导,在实际的软件开发中,必须根据具体的需求来选择:
- 对于简单的程序,可能写一个简单的算法要比引入某种设计模式更加容易;
- 但是对于大型项目开发或者框架设计,用设计模式来组织代码显然更好。
本系列文章虽然命名为“Java设计模式”,但是设计模式并不是 Java 的专利,它同样适用于 C++、C#、JavaScript 等其它面向对象的编程语言。Java 是典型的面向对象的编程语言,所以本系列文章以 Java 为基础来讲解这 23 种设计模式.
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。