一道关于单例模式的题
本帖最后由 daituzhang 于 2013-09-05 07:37:33 编辑 我是java零基础者,靠google和baidu在解这道题,如果有低级错误望各位大神抽打
因为题目原文是英文,我翻译可能有点不准确:
1)建立一个叫做Organization的类,它不能被实例化超过一次但是可以提供给全局。它包含仅一个叫做getMembers 的public method,返回一个collection.
2) 建立另一个叫做Member的类,publicly exposes(公共得包括?) FirstName, lastName 和 DateJoined bean properties,请使用合适的数据类型
3)建立第三个叫做OrganizationHelper的类,包含一个method用于输出Organization类中所有Members的数据。输出的类型随意。
然后我就开始纠结了,第一小题我百度下来应该是个单例模式的问题,collection我觉着有点像c++里面的containers,我就用了ArrayList.但是这个ArrayList到底怎么获取member的信息我却想不清楚。然后因为根据单例模式,这个类必须向整个系统提供这个实例对象,那么要怎么同时又返回一个collection呢?还是说这个实例对象本身就是一个collection?所以我两个method就分开了,只有getMember设成了public
第二小题我又纠结什么是bean property,百度到javaBean但是好像是和html结合起来用的,我是不是对bean property这两个单词过度理解了?
第三小题我只是放了打印的代码……但是那个method名字叫XXhelper,C++面向对象里面helper函数是在类外面的,我不清楚java是不是也有这种区别,所以我就只做了个把它作为像main一样的class了
代码如下,虽然得到了结果,但是我很怀疑错过了几个关键的点
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
class Organization {
ArrayList<Member> List = new ArrayList<Member>();
void add(Member temp)
{
List.add(temp);
}
private static Organization instance=null;
private Organization(){}
static Organization getInstance(){
if(instance==null)
instance=new Organization();
return instance;
}
public ArrayList<Member> getMembers()
{
return List;
}
}
class Member
{
public String firstName;
public String lastName;
public Date DateJoined;
public void set( String firstName, String lastName, Date DateJoined)
{
this.firstName = firstName;
this.lastName = lastName;
this.DateJoined = new Date( DateJoined.getTime() );
}
public String getFirstName()
{
return this.firstName;
}
public String getLastName()
{
return this.lastName;
}
public Date getDateJoined()
{
return new Date( this.DateJoined.getTime() );
}
}
public class OrganizationHelper{
public static void main(String args[]){
Organization test=Organization.getInstance();
Member member1 = new Member();
Member member2 = new Member();
Date date1 = null;
Date date2 = null;
SimpleDateFormat fmt=new SimpleDateFormat("yyyy-MM-dd");
try {
date1 = fmt.parse("1992-06-12");
date2 = fmt.parse("2001-03-03");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
member1.set("Tom","Cruise",date1);
member2.set("Xiao","Lizi",date2);
test.add(member1);
test.add(member2);
ArrayList<Member> List = test.getMembers();
for(int i=0;i<List.size();i++)
{
System.out.println(List.get(i).firstName);
System.out.println(List.get(i).lastName);
System.out.println(List.get(i).DateJoined);
}
}
}
[解决办法]
1) 这题目的意思是实例化控制,实例化控制在Java里有两种常用手段:
a. enum 类型天生自带实例化控制,并且对反射、反序列化之类的hack免疫,
b. 普通类可以通过限制【构造方法】的可见性来控制实例化(可以被反射、反序列化之类的手段hack,不过这里hack应该不在考虑之列),比如把构造方法定义为 private。 (Java中如果你不写构造方法,有一个默认的 public 的无参构造方法)
实例化控制中,一种情况是全局只允许产生一个实例,即通常说的“单例”。
所以 1) 大概是下面这样:
import java.util.Collection;
import java.util.Collections;
public class Organization {
private static Organization INSTANCE = new Organization();
public static Organization getInstance() {
return INSTANCE;
}
private Collection<Member> members;
private Organization() {
// TODO ...
}
public Collection<Member> getMembers() {
return Collections.unmodifiableCollection(members);
}
}
用户代码获取 Member 大概是这样的:
for(Member member : Organization.getInstance().getMembers()) {
// TODO ...
}
[解决办法]
我不是大神。。。我说说我的看法
第一题
单利模式getInstance方法得加上public,才能提供给全局,就是用类名直接访问。
第二题
bean这里应该就是指实体类Member吧。。。
Member里加上有参构造方法和无参构造方法,set和get方法
DateJoined直接赋值就行了
第三题
OrganizationHelper属于工具类,加个静态的打印方法就行了。
具体的我试着写了下:
import java.util.ArrayList;
import java.util.Date;
public class Organization {
private ArrayList<Member> List = new ArrayList<Member>();
public void addMember(Member temp)
{
List.add(temp);
}
private static Organization instance=null;
private Organization(){}
public static Organization getInstance(){
if(instance==null)
instance=new Organization();
return instance;
}
public ArrayList<Member> getMembers()
{
return List;
}
}
class Member
{
public String firstName;
public String lastName;
public Date dateJoined;
public Member() {
}
public Member( String firstName, String lastName, Date dateJoined)
{
this.firstName = firstName;
this.lastName = lastName;
this.dateJoined = dateJoined;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getDateJoined() {
return dateJoined;
}
public void setDateJoined(Date dateJoined) {
this.dateJoined = dateJoined;
}
}
public class OrganizationHelper{
public static void showInfo(Organization organization) {//打印方法
List<Member> members = organization.getMembers();
for(int i=0; i<members.size(); i++) {
System.out.println(members.get(i).firstName);
System.out.println(members.get(i).lastName);
System.out.println(members.get(i).dateJoined);
}
}
public static void main(String args[]){//入口函数放哪里都可以
Organization test=Organization.getInstance();
Date date1 = null;
Date date2 = null;
SimpleDateFormat fmt=new SimpleDateFormat("yyyy-MM-dd");
try {
date1 = fmt.parse("1992-06-12");
date2 = fmt.parse("2001-03-03");
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Member member1 = new Member("Tom","Cruise",date1);
Member member2 = new Member("Xiao","Lizi",date2);
test.addMember(member1);
test.addMember(member2);
OrganizationHelper.showInfo(test);
}
}[解决办法]
2) bean,所谓 java bean, POJO, entity 等等这些名词,通常是指最简单的类: 有成员,每个成员有 getter 和 setter 方法,并且这些 getter 和 setter 方法都按规则命名: X 成员的 getter 方法就是 public X getX();
这种命名规范是为了对反射友好,当你知道这个类有一个 Field 是 String firstName, 你就知道这个类必然有方法: public void setFirstName(String) 来给 firstName 赋值。
对反射友好,所以其他的代码(通常是框架实现的代码)就能用反射的方式对这种类的对象进行序列化与反序列化,常用的存储对象的方式是 XML, 所以你“百度到javaBean但是好像是和html结合起来用的”
比如这样写的 Member 类:
import java.util.Date;
public // <-- publicly exposes
class Member {
private String firstName;
private String lastName;
private Date joinedDate;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getJoinedDate() {
return joinedDate == null ? null : new Date(joinedDate.getTime());
}
public void setJoinedDate(Date joinedDate) {
this.joinedDate = joinedDate == null ? null : new Date(joinedDate.getTime());
}
}
存储成 XML 的格式可以这样:
/*
* <Member>
* <FirstName>Raistlin</FirstName>
* <LastName>Majere</LastName>
* <JoinedDate>2013-09-05</JoinedDate>
* </Member>
*/
[解决办法]
3) 在 2) 中的实体类只做存储信息之用,所以只有 getter 和 setter 方法,这里的 helper 类就是所谓“业务逻辑”了,业务逻辑和实体类分开,是为了更清晰的层面,以及实体类的复用。
比如有下面的 memberlist.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Member>
<FirstName>123</FirstName>
<LastName>A</LastName>
<JoinedDate>2013-09-05</JoinedDate>
</Member>
<Member>
<FirstName>124</FirstName>
<LastName>B</LastName>
<JoinedDate>2013-09-05</JoinedDate>
</Member>
<Member>
<FirstName>125</FirstName>
<LastName>C</LastName>
<JoinedDate>2013-09-05</JoinedDate>
</Member>
<Member>
<FirstName>126</FirstName>
<LastName>D</LastName>
<JoinedDate>2013-09-05</JoinedDate>
</Member>
<Member>
<FirstName>127</FirstName>
<LastName>E</LastName>
<JoinedDate>2013-09-05</JoinedDate>
</Member>
</root>
简单完成 Organization 类如下:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class Organization {
private static final Organization INSTANCE = new Organization();
public static Organization getInstance() {
return INSTANCE;
}
private Collection<Member> members;
private Organization() {
members = loadMembers();
}
private Collection<Member> loadMembers() {
try {
List<Member> result = new ArrayList<Member>();
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
getClass().getResourceAsStream("memberlist.xml"));
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
NodeList list = doc.getDocumentElement().getChildNodes();
Map<String, Field> fields = fieldsOf(Member.class);
for(int k=0; k<list.getLength(); k++) {
Node node = list.item(k);
if( !node.getNodeName().equals("Member") )
continue;
Member member = Member.class.newInstance();
NodeList l = node.getChildNodes();
for(int j=0, len=l.getLength(); j<len; j++) {
Node m = l.item(j);
String name = m.getNodeName();
Field field = fields.get(name);
if( field == null )
continue;
String methodName = "set" + name;
if( field.getType().equals(Date.class) ) {
Method method = Member.class.getMethod(
methodName, new Class<?>[]{Date.class});
Date value = format.parse(m.getTextContent());
method.setAccessible(true);
method.invoke(member, value);
}
else {
Method method = Member.class.getMethod(
methodName, new Class<?>[]{String.class});
method.setAccessible(true);
method.invoke(member, m.getTextContent());
}
}
result.add(member);
}
return result;
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private Map<String, Field> fieldsOf(Class<?> type) {
Map<String, Field> result = new HashMap<String, Field>();
Field[] fields = type.getDeclaredFields();
for(Field f : fields)
result.put(Character.toUpperCase(f.getName().charAt(0)) + f.getName().substring(1), f);
return result;
}
public Collection<Member> getMembers() {
return Collections.unmodifiableCollection(members);
}
}
那么 OrganizationHelper 类大概是这样的:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
public class OrganizationHelper {
public void print(Member member) {
if( member == null )
throw new NullPointerException();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
System.out.printf("%-15s%s%n", "First Name:", member.getFirstName());
System.out.printf("%-15s%s%n", "Last Name:", member.getLastName());
System.out.printf("%-15s%s%n", "Joined Date:", format.format(member.getJoinedDate()));
System.out.println();
}
public static void main(String[] args) {
OrganizationHelper helper = new OrganizationHelper();
for(Member m : Organization.getInstance().getMembers())
helper.print(m);
}
}
[解决办法]
是的,详见7楼示例代码。
实际应用中一般不用自己调用 XML parsing 的库—OM,JAX……),因为 Java 内建了 JAXB,通过给类加适当的 annotation,就可以用 JAXB 自动加载对象或把对象写成xml。 另外像 Spring 这样的框架也可以帮你完成。
[解决办法]
1 - 是的,所以这里你需要写 private Organization() { ... }
2 - 是的, getInstance 是静态方法,每次调用都是返回那个唯一的实例
3 - 见 7 楼
4 - Collection 是更抽象的接口“集合”,比如可以是 tree set, 可以是 hash map,也可以是 list 或者 array,它定义的只是所有“集合”都应该具备的行为
Collections.unmodifiableCollection 的作用相当于 defensive copy,加一层“壳”变成只读,只是不想让外部代码随意更改 Organization 的内部状态
5 - 是,见 7 楼
[解决办法]
DateJoined bean properties是什么不懂,其它我就按字面意思做,如下: