------- android培训、java培训、期待与您交流! ----------
设计模式总结(已学的,不全)
设计模式是为了解决某一类问题最行之有效的方法。下面是我看视频学习的设计模式的总结。
1、单例设计模式
目的:解决一个类在内存中只存在一个对象。
1)禁止其他程序建立该对象。
2)在本类中自定义一个对象,让其他程序访问。
3)提供一些访问方式,使其他程序访问。
解决方法:
1)将构造函数私有化。
2)在类中创建一个本类对象
3)提供一个方法可以获取到该对象。
分为两种具体解决方案:1、饿汉式
class Single{
private Single(){}
private static Single s = new Single();
public static Single getInstance(){
return s;
}
}
饿汉式是类一加载到内存是就创建了本类对象。
2、懒汉式
class Single{
private Single(){}
private static Single s = null;
public static Single getInstance(){
if(s == null){
synchronized(Single.class){ //加锁,防止多个程序进入,都创建对象
if(s == null)
s = new Single();
}
}
return s;
}
}
懒汉式是类加载时只创建本类引用,当需要获取实例对象是再创建,称为对象的延迟加载。懒汉式的缺点是在获取对象时需要加锁,防止多个程序进入,都创建对象。这样会让程序效率变低。所以在实际开发中建议使用饿汉式。
注意:面试时会考到懒汉式。
2、装饰设计模式
当想要对已有的对象进行功能加强时,可以自定义类,将已有的对象传入,基于已有的功能,并提供加强功能,那么这个自定义的类就称为装饰类。
装饰类通常会通过构造方法来接收被修饰的对象,并基于被装饰对象的功能,提供更强的功能。装饰比继承更加灵活,降低了类与类之间的关系。
下面是装饰类的例子:
class MyBufferedReader
{
//Reader类中的read方法只能单个字符的读取,效率比较低
private Reader r;
//将Reader类对象作为被装饰类对象传递给MyBufferedReader的构造方法中
MyBufferedReader(Reader r)
{
this.r = r;
}
//基于read方法提供readline方法,是效率增强
public String myReadLine()throws IOException
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = r.read()) != -1)
{
if(ch == '\r')
continue;
if(ch == '\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length() != 0)
{
return sb.toString();
}
return null;
}
public void myClose()throws IOException
{
r.close();
}
}
public class MyBufferedReaderTest
{
public static void main(String[] args)
{
MyBufferedReader bufr =null;
try
{
bufr = new MyBufferedReader(new FileReader("buffer.txt"));
String line = null;
while((line = bufr.myReadLine())!= null)
{
System.out.println(line);
}
}
catch(IOException e)
{
throw new RuntimeException("读写异常");
}
finally
{
try
{
if(bufr != null)
bufr.myClose();
}
catch(IOException e)
{
throw new RuntimeException("读写异常");
}
}
}
}
红色字体的代码为装饰设计模式的举例,将Reader类作为被装饰类,将其对象作为参数传递给MyBufferedReader类的构造方法中,在此类中提供功能更强大的读取方法,实现读取一行的功能。
3、享元设计模式
先举个例子引出享元设计模式:
Integer a = 56;
Integer b = 56;
System.out.println( a == b);结果为true。这是因为当创建Integer对象时,数值大小在一个byte(即-128~127)范围之间时,把对象存放到data区中,当再次创建另一个相同数值的对象时,不再创建新的对象,而是把引用指向已有的对象。
这种现象就是所说的享元设计模式,即当某些对象的属性相同时,不再重复创建,而是把其缓存起来,只将引用指向其已存在的对象即可。这样的设计模式的好处就是,可以节省内存空间。
4、状态模式
我们由一个例子引出状态模式:
class Actor{
public void act(){}
}
class HappyActor extends Actor{
public void act(){
System.out.println("HappyActor");
}
}
class SadActor extends Actor{
public void act(){
System.out.println("SadActor");
}
}
class Stage{
private Actor actor = new HappyActor();
public void change(){
actor = new SadActor();
}
public void performPlay(){
actor.act();
}
}
public class Transmogrify {
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
当你在进行程序设计的时候,首先最好的选择是组合,尤其是不能十分确定应该使用哪一种方式时。组合不会强制我们的程序设计进入继承的层次结构中。且组合更加灵活,可以动态选择类型,相反,继承要在编译时就知道确切类型。
上面的例子中,Stage类中包含一个Actor引用,首先赋值给HappyActor对象。这是performPlay方法会产生特殊的行为。引用也可以在运行时与另外一个不同的对象重新绑定,当actor被重新赋值为SadActor时,performPlay方法有相应产生其特殊行为。这就是所谓的状态模式,即在运行期间获得动态灵活性。而我们不能在运行时决定继承不同的对象,因为它要求在编译期就完全确定下来。
5、策略设计模式
import java.util.*;
class Processor{
public String name(){
return getClass().getSimpleName();
}
Object process(Object input){
return input;
}
}
class Upcase extends Processor{
String process(Object input){
//协变返回类型
return ((String)input).toUpperCase();
}
}
class Downcase extends Processor{
String process(Object input){
return ((String)input).toLowerCase();
}
}
class Splitter extends Processor{
String process(Object input){
return Arrays.toString(((String)input).split(" "));
}
}
public class Apply {
public static void process(Processor p,Object s){
System.out.println("Using Processor " + p.name());
System.out.println(p.process(s));
}
public static String s = "Welcome to Heima,good good study,day day up";
public static void main(String[] args) {
process(new Upcase(),s);
process(new Downcase(),s);
process(new Splitter(),s);
}
}
上例中Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后相应打印结果。像这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。这种方法包含所要执行的算法中固定不变的部分,而“策略”就是包含变化的部分,即传进去的参数对象,它包含要执行的代码。Processor对象就是一个策略,在main()中可以看到三种不同类型的策略应用到了String类型的s对象上。
6、适配器设计模式
我们拿Scanner类举例,通过查API知道,Scanner类的构造器接受的是一个Readable接口。我们想要实现以下功能,创建一个随机产生Double类型的数的RandomDoubles类。
import java.util.*;
public class RandomDoubles {
private static Random rand = new Random(47);
public double next(){
return rand.nextDouble();
}
public static void main(String[] args) {
RandomDoubles rd = new RandomDoubles();
for (int i = 0; i < 7 ; i++) {
System.out.println(rd.next());
}
}
}
这个程序可以产生一个随机Double数,那么我们如何让他作用于Scanner上呢?这是我们就用到了适配器设计模式,创建一个适配器类,该适配器类实现Readable接口,此时我们生成的是一个既是RandomDoubles又是Readable的新类,可以作为Scanner的参数传递进去。
import java.nio.CharBuffer;
import java.util.*;
public class AdaptedRandomDoubles extends RandomDoubles implements Readable{
private int count;//计数,产生几个随机数
public AdaptedRandomDoubles(int count){
this.count = count;
}
//重写Readable接口的read()方法
public int read(CharBuffer cb){
if(count-- == 0)
return -1;
String result = Double.toString(next())+" ";
cb.append(result);
return result.length();
}
public static void main(String[] args) {
Scanner s = new Scanner(new AdaptedRandomDoubles(7));
while(s.hasNextDouble()){
System.out.println(s.nextDouble() + " ");
}
}
}
所谓的适配器模式就是,当我们无法修改你想要使用的类时,如RandomDoubles类,我们就要用到适配器设计模式,适配器中的代码将接受你所拥有的接口,实现接口中的方法,并产生你所需要的接口,如AdaptedRandomDoubles。
7、工厂方法设计模式
接口可以用来实现多重继承,我们生成遵循某个接口的对象的典型方式就是工厂方法设计模式。这与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上,这种方式将完全与接口的实现分离,使得我们可以透明的将某个实现替代另一个实现。
下面的例子展示工厂方法的结构:
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Implementation1 implements Service{
public Implementation1() {}
public void method1(){
System.out.println("Implementation1 method1");
}
public void method2(){
System.out.println("Implementation1 method2");
}
}
class Implementation1Factory implements ServiceFactory{
public Service getService(){
return new Implementation1();
}
}
class Implementation2 implements Service{
public Implementation2() {}
public void method1(){
System.out.println("Implementation2 method1");
}
public void method2(){
System.out.println("Implementation2 method2");
}
}
class Implementation2Factory implements ServiceFactory{
public Service getService(){
return new Implementation2();
}
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact){
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implementation1Factory());
serviceConsumer(new Implementation2Factory());
}
}
下面我们把上面的例子用匿名内部类来简化。
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Implementation1 implements Service{
private Implementation1(){}
public void method1(){
System.out.println("Implementation1 method1");
}
public void method2(){
System.out.println("Implementation1 method2");
}
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation1();
}
};
}
class Implementation2 implements Service{
private Implementation2(){}
public void method1(){
System.out.println("Implementation2 method1");
}
public void method2(){
System.out.println("Implementation2 method2");
}
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation2();
}
};
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact){
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(Implementation1.factory);
serviceConsumer(Implementation2.factory);
}
}
Implementation1和Implementation2的构造方法都是private的,并且我们没有必要创建作为工厂的具名类,用匿名类很合适。我们经常使用单一的工厂对象,所以用static修饰。