2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 设计模式——概要与设计原则

设计模式——概要与设计原则

时间:2024-07-12 02:24:04

相关推荐

设计模式——概要与设计原则

最近在学习设计模式,设计模式是基础知识,是学习框架等的利器,并且设计模式本身设计很巧妙,研究起来颇有意思!想以写博客的形式把所学的内容记录下来算是总结,以便日后的复习与交流!

设计模式(Design pattern)

是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

四要素:

设计模式使人们可以更加简单方便地复用成功的设计和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。

模式名称(Pattern Name):帮助记忆的名称,技术术语,便于设计者之间的交流

问题(Problem):描述设计者所面临的场景,用于告诉设计者在什么情况下使用

解决方案(Solution):描述了设计的细节,包括原理图示(UML类图,序列图)和有关的文字说明

效果(Consequences):描述设计方案的优点缺点,如扩展性,可复用性

常见的设计模式可分为三种类型,共23种

创建型模式:创建型模式用来处理对象的创建过程

单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
结构型模式:结构型模式用来处理类或者对象的组合;

适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
行为型模式:行为型模式用来对类或对象怎样交互和怎样分配职责进行描述。

模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

面向对象的设计原则:

单一职责(Single Responsibility Principle):

定义;一个类,应该只专注做一件事,只有单一原因引起该类的变化,即自己只做自己的事情。

如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性。比如

/*** 图片加载类*/public class ImageLoader {// 图片缓存LruCache<String, Bitmap> mImageCache;// 线程池,线程数量为CPU的数量ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());public ImageLoader() {initImageCache();}private void initImageCache() {// 计算可使用的最大内存final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);// 取四分之一的可用内存作为缓存final int cacheSize = maxMemory / 4;mImageCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight() / 1024;}};} public void displayImage(final String url, final ImageView imageView) {imageView.setTag(url);mExecutorService.submit(new Runnable() {@Overridepublic void run() {Bitmap bitmap = downloadImage(url);if (bitmap == null) {return;}if (imageView.getTag().equals(url)) {imageView.setImageBitmap(bitmap);}mImageCache.put(url, bitmap);}});}public Bitmap downloadImage(String imageUrl) {Bitmap bitmap = null;try {URL url = newURL(imageUrl);final HttpURLConnection conn = (HttpURLConnection)url.openConnection();bitmap = BitmapFactory.decodeStream(conn.getInputStream());conn.disconnect();} catch (Exception e) {e.printStackTrace();}return bitmap;}}

这是没有遵循单一职责的代码,按照单一设计职责,当用户使用的是图片加载类时,只会关心图片加载这个功能,所以图片加载类应该只应该具备图片加载这个功能,然而代码中还加入的图片缓存的功能,这个不是用户所关心的问题。再者,这样的代码不利于后期维护和升级。接下来看看利用单一职责优化之后的代码。

/*** 图片加载类*/public class ImageLoader {// 图片缓存ImageCache mImageCache = new ImageCache() ;// 线程池,线程数量为CPU的数量ExecutorService mExecutorService = Executors.newFixedThreadPool (Runtime.getRuntime().availableProcessors());// 加载图片public void displayImage(final String url, final ImageView imageView) {Bitmap bitmap = mImageCache.get(url);if (bitmap != null) {imageView.setImageBitmap(bitmap);return;}imageView.setTag(url);mExecutorService.submit(new Runnable() {@Overridepublic void run() {Bitmap bitmap = downloadImage(url);if (bitmap == null) {return;}if (imageView.getTag().equals(url)) {imageView.setImageBitmap(bitmap);}mImageCache.put(url, bitmap);}});}public Bitmap downloadImage(String imageUrl) {Bitmap bitmap = null;try {URL url = new URL(imageUrl);final HttpURLConnection conn = (HttpURLConnection) url.openConnection();bitmap = BitmapFactory.decodeStream(conn.getInputStream());conn.disconnect();} catch (Exception e) {e.printStackTrace();}return bitmap;}}

这个是图片缓存类:

public class ImageCache {// 图片LRU缓存LruCache<String, Bitmap> mImageCache;public ImageCache() {initImageCache();}private void initImageCache() {// 计算可使用的最大内存final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);// 取四分之一的可用内存作为缓存final int cacheSize = maxMemory / 4;mImageCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {return bitmap.getRowBytes() * bitmap.getHeight() / 1024;}};}public void put(String url, Bitmap bitmap) {mImageCache.put(url, bitmap) ;}public Bitmap get(String url) {return mImageCache.get(url) ;}}

经过这样处理,将共功能拆分之后,职责变得更清晰,更利于后期代码的维护和修改。(例子来源于Hi小鱼设计原则的分享)

依赖倒转(Dependence Inversion Principle):

定义:高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

这个原则很好的体现者“策略模式”,策略模式将算法的实现与算法的使用者独立,让使用算法的客体不受具体算法的影响!这里举一个简单的例子帮助理解依赖倒转原则,也帮助以后很好的掌握策略模式。例如美国的收税问题,要做一个计算A州收税的计算工具,并实现了如下代码:

class TaxA {public void count() {System.out.println("A州计算税...");}}class TaxCalculator {public void calculat(TaxA tax) {tax.count();}}public class Client {public static void main(String[] args) {TaxCalculator tc = new TaxCalculator();TaxA a = new TaxA();tc.calculat(a);}}

计算器能很好的工作,随着需求的增长要扩充计算器的适用范围来包含其他州的税收。于是开始制作并得到了如下代码:

/*** A州税收类* * @author Administrator**/class TaxA {public void count() {System.out.println("A州计算税...");}}/*** B州税收类* * @author Administrator**/class TaxB {public void count() {System.out.println("B州计算税...");}}/*** 计算各州类的方法集合体* * @author Administrator**/class TaxCalculator {public void calculat(TaxA tax) {tax.count();}public void calculat(TaxB tax) {tax.count();}}/*** 测试* * @author Administrator**/public class Client {public static void main(String[] args) {TaxCalculator tc = new TaxCalculator();TaxB b = new TaxB();tc.calculat(b);}}

通过添加几个方法和几个类就能完成,而且计算器依然能很好的工作!随着需求的进一步增加,需要将计算器的范围进一步扩大遍及所有的州,这时通过不断的增加方法和类的途径,不但要修改原有的代码,而且整体系统变得比较臃肿,出现这种情况的原因是TaxCalculator(上层类)依赖于TaxXxx(下层类),具体类的各种变化对上层类的影响很大,这种方法不是很好的设计!如下是结构优化后的代码:

/*** 抽象出Tax税收接口* * @author Administrator**/interface Tax {public void count();}/*** 具体州的税实现Tax* * @author Administrator**/class TaxA implements Tax {public void count() {System.out.println("A州计算税率...");}}class TaxB implements Tax {public void count() {System.out.println("B州计算税率...");}}class TaxC implements Tax {public void count() {System.out.println("C州计算税率...");}}// 省略其他州....../*** 不需要知道具体是哪个州只需要知道该州实现了Tax接口即可* * @author Administrator**/class TaxCalculator {public void calculat(Tax tax) {tax.count();}}/*** 测试* * @author Administrator**/public class Client {public static void main(String[] args) {TaxCalculator tc = new TaxCalculator();Tax tax = new TaxC();tc.calculat(tax);}}

这样我们就不用一直修改代码来添加功能了,只要让各个州的税收类实现Tax接口即可,是不是感觉轻松了很多,计算器不需要知道具体计算的是哪个州的税,只需要知道该类实现了Tax接口,那么计算器就能计算出该州的税了!这么一来原本依赖于具体税收类TaxA(、TaxB...)的TaxCalculator计算器,现在变成具体税收类TaxA(、TaxB...)和TaxCalculator计算器都只依赖了Tax接口了。这样的设计就是依赖倒转的体现!

里氏代换(Liskov Substitution Principle):

定义:所有引用父类的地方必须能透明的使用其子类的对象,即可使用上转型对象。

接口隔离(Interface Segregation Principle):

定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

“不应该强迫客户依赖于它们不用的方法。接口属于客户,不属于它所在的类层次结构。”这个说得很明白了,再通俗点说,不要强迫客户使用它们不用的方法,如果强迫用户使用它们不使用的方法,那么这些客户就会面临由于这些不使用的方法的改变所带来的改变。

迪米特法则(Law of Demeter):

定义:不要和陌生人说话,即一个对象应对其他对象有尽可能少的了解。

开闭原则(Open Close Principle):

定义:对扩展开放,对修改关闭。即实现在不改变源代码的情况下改变这个模块的行为。

在软件的生命周期中,难免会有变化、升级和维护等原因需要对原有的代码进行修改时,直接修改原有的代码可能会将新错引入原本已经稳定的系统中,破坏原有的系统。所以软件需要对扩展开放对修改关闭。当满足其他几条设计原则时可以看出一定满足开闭原则,当不满足其他几条原则时很难满足开闭原则,也就是说开闭原则是其他原则的总结。交税的例子同样可以反映出开闭原则,当要有其他的州需要计算税的时候,只需重新创建一个实现了Tax接口的类,将其加入到原有的系统中即可实现扩充,即表现出了对扩展开放,并不需要修改系统原有的代码,即表现出对修改关闭。设计系统时能遵循这些原则可以提高系统的稳定性,同时又能极大的提高系统的可扩展性。

合成复用:又称为组合/聚合复用原则,要尽量使用对象组合,而不是使用继承来达到复用的目的。即尽量使用组合/聚合关系,少使用继承。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。