I want to fly higher
programming Explorer
posts - 114,comments - 263,trackbacks - 0
-序言: 
    1.Java的正式发行名称           工程版本号
      JDK 1.1.x / JRE 1.1.x           1.1
      Java2 Platform,Standard Edition,v 1.2     1.2
      Java2 Platform,Standard Edition,v 1.3     1.2
      Java2 Platform,Standard Edition,v 1.4     1.2
      Java2 Platform,Standard Edition,v 5.0     1.5
      Java Platform,Standard Edition,5       1.6
    2.术语:
         1.Java语言支持4种类型:
              接口(interface) 类(class) 数组(array)  基本类型(primitive),前3中类型通常被称为引用类型,类实例和数组是对象,而基本类型的值则不是对象。
         2.类的成员由其域(field),方法(method),成员类(member class)和成员接口(member interface)组成.
         3.方法的签名(signature)由它的名称和所有的参数类型组成,签名不包括它的返回类型。
         4.继承(inheritance),作为子类化(subclassing)的同义词
         5.不再使用接口继承这种说法,而是简单的说一个类实现(implement)了另一个接口或者说一个接口扩展(extend)了另一个接口.
         6.描述没有指定访问级别的情况下所使用的访问级别,使用了包级私有(package-private),而不是如JLS(Java Language Specifications),6.6.1所使用的技术性术语"缺省访问(default access)"级别.
         7.导出的API(exported API)或者简单的说API,是指类,接口,构造器,成员,和序列化形式,程序员可以通过他们可以访问类,接口够或者包。使用API编写程序的程序员被称为该API的用户(user),在类的实现中使用了API的类被称为该API的客户(client).
         8.类,接口,构造器,成员以及序列化的形式被统称为API元素(API element),导出的API由所有可在该API的包之外访问的API元素组成。任何客户端都可以使用这些API元素,而API的创建者负责支持这些API元素。Javadoc工具类在默认情况下也正是为这些元素生成文档,这绝非偶然。不严格的讲,一个类的导出的API是由每个公有(public)类或者接口中所有公有的或者受保护的(protected)成员和构造器组成.

    第一条:考虑用静态工厂方法代替构造器
 
      1.对于类来说,为了让客户端获取其自身的一个实例,最常用的办法就是提供一个公有的构造器。还有一种方法,也应该在每个程序员的工具箱中占有一席之地。类可以提供一个公有的静态工厂方法(static factory method),其时一个返回类实例的静态方法。如:
   public static Boolean valueOf(boolean b)
   {
    return b ? Boolean.TRUE : Boolean.FALSE;
   }
      :静态工厂方法与设计模式的工厂方法模式不同。本条目所指的静态工厂方法并不直接对应设计模式的工厂方法。 类可以通过静态工厂方法提供他的客户端,而不是通过提供构造器
     2.优势:
          1.静态工厂方法与构造器不同的第一大优势在于:他们有名称。如果构造器的参数本身没有确切的描述正被返回的对象,那么具有适当名称的静态工厂方法会更容易使用,产生的客户端代码也更易读。
  如:BigInteger(int,int,Random),返回的可能是素数,如果用BigInteger.probablePrime的静态工厂方法来表示,显示更清楚。1.4的发行版本中最终增加了这个方法。
          2.一个类只能有一个带有指定签名的构造器,编程人员通常避开这一限制是提供两个构造器,而它们的参数列表只是在参数列表顺序上有所不同。实际上这不好。对于这样的API,client永远也记不住该调用哪个构造器,结果通常会调用错误的构造器。并且读到这些构造器的代码时,如果没有参考文档,也不知所云。所以由于静态工厂方法有名称,所以他们不受上述限制。当一个类需要多个带有相同签名的构造器(只是参数列表顺序不同)时,就用静态工厂方法代替构造器并且慎重选择名称已突出他们之间的区别。
          3.静态工厂方法与构造器不同的第二大优势在于,不必再每次调用它们的时候都创建一个新对象。
               这使得不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)说明了这项技术,它从来不创建对象。
   这类似于Flyweight模式。如果程序经常请求创建相同的对象,并且创建对象的代价很高,则这项技术可以极大的提高性能。
   静态工厂方法能够为重复的调用返回相同对象,这有助于类总能严格控制在某个时刻那些实例应该存在。这种类被称为实例受控的类。instance-controlled.编写实例受控的类有几个原因:
                1.使得类可以确保它是一个单例或者是不可实例化的
                2.使得不可变的类确保不会存在两个相等的实例,即当且仅当a == b的时候才有a.equals(b)为true.如果类保证了这一点,其客户端就可以利用==操作符来代替equals(Object)方法,这样可以提高性能。枚举类型保证了这一点。
          4.静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型对象:
               这样我们在选择返回对象的类时就有了更大的灵活性.
               这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的,以这种方式因此实现类会使API变的非常简洁.该项技术适用于基于接口的框架,interface-based-framework.
   接口为静态工厂方法提供了自然返回类型。因接口中不能有静态方法,按照惯例,接口Type的静态工厂方法被放在一个名为Types的不可实例化的类中。
           例子1:
                Java Collections Framework的集合接口有32个便利实现,分别提供了不可修改的集合,同步集合等。几乎所有这些实现都通过静态工厂方法在一个不可实例化的类java.utils.Collections中导出。所有返回对象都是非公有的。
    如:
      public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c)
      {
            return new UnmodifiableCollection<>(c);
       }
        
       static class UnmodifiableCollection<E> implements Collection<E>, Serializable
       UnmodifiableCollection定义在Collections中,是非public。
           现在Collections Framework Api被导出32个独立公有类的那种实现方式要小的多,每种便利实现都对应一个类。这不仅仅是指API数量上的减少,也是概念意义上的减少,用户知道被返回的对象是由相关的接口精确指定的,所以它们不需要阅读相关的类文档。使用这种静态工厂方法时,甚至要求客户端通过接口来引用被返回的对象,而不是通过它的实现类来引用被返回的对象,这是一种良好的习惯。
       公有的静态工厂方法所返回的对象的类不仅可以是非公有的,而且该类还可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。只要是已生命的返回类型的子类型,都是允许的。
       为了维护软件的可维护性和性能,返回的对象的类也可能随着发行版本的不同而不同。
       如:java.util.EnumSet,没有公用构造器,只有静态工厂方法。他们返回两种实现类之一,具体取决于底层枚举数组的大小;如果它的元素有64个或者更少,就像大多数枚举类型一样,静态工厂方法返回一个RegularEnumSet实例,用单个long支持;如果枚举类型有65个或者更多,工厂就返回JumboEnumSet实例,用long数组进行支持。
       如:
        public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)
        {
            Enum[] universe = getUniverse(elementType);
            if (universe == null)
                throw new ClassCastException(elementType + " not an enum");
    
            if (universe.length <= 64)
                return new RegularEnumSet<>(elementType, universe);
            else
                return new JumboEnumSet<>(elementType, universe);
        }
           这两个实现类的存在对于客户端来说是不可见的。如果RegularEnumSet不能再给小的枚举类型提供性能优势,就可能从未来的发行版本中将它删除,不会造成不良的影响。同样的了,如果证明对性能要好处,也可能在未来的发行版本中添加第三甚至第四个Enum实现。客户端永远也不知道也不关心他们从工厂方法得到的对象的类,他们只关心他是EnumSet的某个子类即可。
       class RegularEnumSet<E extends Enum<E>> extends EnumSet<E>,非public.
       class JumboEnumSet<E extends Enum<E>> extends EnumSet<E>,非public.
           静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在。这种灵活的静态工厂方法构成了服务提供者框架,Service Provider Framework的基础。如JDBC API。服务提供者框架是指这样一个系统,多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,把它们从多个实现中解耦出来。
           服务器提供者框架中有三个重要的组件:服务接口(Service Interface),这是提供者实现的,提供者注册API,provider registration API,这是系统用来注册实现,让客户端访问它们的;服务访问API,Service Access API,是客户端用来获取服务实例的。服务访问API一般允许但是不要求客户端指定某种选择提供者的条件.如果没有这样的规定,API就会返回默认实现的 一个实例。服务访问API是灵活的静态工厂,它构成了服务提供者框架的基础.
       服务提供者框架的第四个组件是可选的:服务提供者接口,Service Provider Interface,这些提供者负责创建器服务实现的实例。如果没有服务提供者接口,实现就按照类名来注册,并通过反射方式进行实例化。对于JDBC来说,Connection就是它的服务接口,DriverManager.registerDriver就是提供者注册API,DriverManager.getConnection是服务访问API,Driver就是服务提供者接口。
       服务提供者框架模式有着无数种变体.,如服务访问API可以利用适配器模式,返回被提供者需要的更丰富的服务接口:
     5.静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,他们使代码变得更加简洁。
          如在使用参数化的类的构造器时,即时类型参数很明显,也必须指明。如:通常要求你接连两次提供类型参数.
      Map<String,List<String>> m = new HashMap<String,List<String>>
      随着类型参数越来越长,越来越复杂,这一冗长的说明也很快变的痛苦起来。如果有静态工厂方法,编译器就可以替你找到类型参数。这被称为类型推倒,type inference.如假设HashMap提供了这个工厂工厂:
      public static <K,V> HashMap<K,V> newInstance()
      {
       return new HashMap<K,V>();
      }
      你就可以简洁的代码替换上面繁琐的说明:
       Map<String,List<String>> m = HashMap.newInstance();
      目前1.6版本的标准集合还没有提供工厂方法,但是可以把这些方法放在你自己的工具类中。更重要的是,可以把这样的静态工厂放在你自己的参数化的类中。
     6.静态工厂方法的主要缺点在于,类如果不含公有或者受保护的构造器,就不能被子类化。
          对于公有的静态工厂所返回的非公有类,也同样如此。如,你想讲Collections Framework中的任何方便的实现类子类化,是不可能的。
          但是这样有时候也有好处,即它鼓励程序员使用复合compostion,而不是继承。
     7.静态工厂方法的第二个缺点在于,他们与其他的静态方法实际上没有任何区别。在API文档中,他们没有像构造器那样在API文档中明确标识出来,因此对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类,这是非常困难的。javadoc工具总有一天会注意到静态工厂方法。同时,你通过在类或者接口注释中关注静态工厂,并遵守标准的命名习惯,也可以弥补这一劣势。下面是静态工厂的一些惯用名称:
          1.valueOf-不太严格讲,该方法返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法。
          2.of-valueOf的一种更为简洁的替代,在EnumSet中使用并流行起来。
          3.getInstance,返回的实例是通过方法的参数来描述的。但是不能够说与参数具有同样的值。对于单例Singleton来说,该方法没有参数,并返回唯一的实例。
          4.newInstance,向getInstance一样,但newInstance能够确保返回的每个实例都与所有其他实例不同。
          5.getType,就像getInstance一样,但是在工厂方法处于不同的类的时候使用(子类)。Type表示工厂方法所返回的对象类型。
          6.newType,就像newInstance一样,但是在工厂方法处于不同的类的时候使用(子类)。Type表示工厂方法所返回的对象类型。
         总之,静态工厂方法与公共构造器都各有好处,我们需要理解他们各自的长处。静态工厂通常更加合适,因此切记第一反应就是提供公有的构造器,而不先考虑静态工厂。
部分源码:
package com.book.chap2.staticFactory;

/**
*
*服务接口
*Service interface
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-24
*
*/


public interface Service
{

}


package com.book.chap2.staticFactory;

/**
*
*服务提供者接口
*Service Provider interface
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-24
*
*/


public interface Provider
{
    Service newService();
}


package com.book.chap2.staticFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
*
*非实例化的类,用来注册和访问服务
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-24
*
*/


public class Services
{
   
private Services()
   
{
    }

   
   
//服务提供者map
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
   
//默认的服务提供者名字
    public static final String DEFAULT_PROVIDER_NAME = "<def>";
   
   
/**
     *
     * 注册默认的Provider
     *
     * @param p
    
*/

   
public static void registerDefaultProvider(Provider p)
   
{
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }

   
   
/**
     *
     * 注册Provider
     *
     * @param name
     * @param p
    
*/

   
public static void registerProvider(String name,Provider p)
   
{
        providers.put(name, p);
    }

   
   
//服务访问api Service access API
    public static Service newInstance()
   
{
       
return newInstance(DEFAULT_PROVIDER_NAME);
    }

   
   
public static Service newInstance(String name)
   
{
        Provider p = providers.get(name);
       
       
if(p == null)
       
{
           
throw new IllegalArgumentException("No Provider registered with name:" + name);
        }

       
       
return p.newService();
    }

}

package com.book.chap2.staticFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
*
*参数化实例,使用静态工厂,会使代码简单很多
*<p>使用类型推断</p>
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-26
*
*/


public class TypeInference
{
   
/**
     *
     * 使用类型推断的初始化HashMap方式
     *
     * @return
    
*/

   
public static <K,V> HashMap<K, V>  newInstance()
   
{
       
return new HashMap<K, V>();
    }

   
   
public static void main(Stringargs)
   
{
       
//普通方式初始化Map
        Map<String, List<String>> commonMap = new HashMap<String, List<String>>();
       
//类型推断的初始化方式
        Map<String, List<String>> inferMap = TypeInference.newInstance();
    }

}

posted on 2013-03-15 14:57 landon 阅读(2327) 评论(2)  编辑  收藏 所属分类: ProgramBook

FeedBack:
# re: Effective Java 2nd笔记1:序言&考虑使用静态工厂方法代替构造器
2013-03-15 16:18 | 全自动端子机
博客风格挺好 围观下  回复  更多评论
  
# re: Effective Java 2nd笔记1:序言&考虑使用静态工厂方法代替构造器
2013-03-16 10:45 | 背道而驰
我最近也在看这本书,不过很多地方看的半懂不懂的!!!  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: