(208)Encapsulate_Collection
封裝群集
讓這個函式回傳該群集的一個唯讀映件(Real-only view),並在這個class提供【新增/刪除】群集元素的函式
classDiagram class Person{ getCourses():Set setCourses(:Set) }
變成
classDiagram class Person { getCourses():Unmodifiable set addCOurse(:Course) removeCourse(:Course) }
動機
class 常常會使用array、list、set來保存實體資料,而如果有照著(206)Encapsulate_Field也會有相對應的(getter/setter)
取值函式(getter)不應該回傳群集本身,這會讓其他人可以可以修改群集內容,而其他人卻不清楚
這就是對使用者暴露過多【物件內部資料結構】的資訊,如果確實是要回傳多個值,他應該避免用戶直接操作物件內保存的群集,並隱藏物件內【與使用者無關】的資料結構
也不應該對(setter)提供整個群集修改,但可以提供Add/Remove操作
如果以上幾點執行到,就可以很好的被封裝起來,降低群集與使用者的耦合度
範例
class Person
{
public List<Course> Courses {get;set;}
}
class Course
{
public string Name {get;set;}
public bool IsAdvanced {get;set;}
}
class simple_Example
{
public void ManipulatingCourses()
{
var kent = new Person {Courses = new List<Course>()};
kent.Courses.Add(new Course { Name = "Smalltalk programming" , IsAdvanced = false});
kent.Courses.Add(new Course { Name = "Appreciatin Single Malts" , IsAdvanced = true});
var refactoringCourse = new Course { Name = "Refactoring" , IsAdvanced = false};
kent.Courses.Add(refactoringCourse);
kent.Courses.Add( new Course { Name = "Brutal Sarcasm" , IsAdvanced = false});
kent.Courses.Remove(refactoringCourse);
}
public void GettingCourseInfo()
{
var kent = new Person();
var advancedCourses = kent.Courses.Count(c => c.IsAdvanced);
}
}
class Person
{
// 步驟二:建立Private 並且get; private set;
public List<Course> Courses {
get {return _courses;}
private set {_courses = value;}
}
// 步驟三;建構式新增
public Person()
{
_courses = new List<Course>();
}
// 步驟一;新增Add
public void AddCourse(Course course){
Courses.Add(course);
}
// 步驟一;新增Remove
public void RemoveCourse(Course course){
Courses.Remove(course);
}
}
class Course
{
public string Name {get;set;}
public bool IsAdvanced {get;set;}
}
class simple_Example
{
public void ManipulatingCourses()
{
// 步驟二:這邊就會出錯,所以新增建構式(步驟三)
//var kent = new Person{Courses = new List<Course>()};
var kent = new Person();
kent.Courses.Add(new Course { Name = "Smalltalk programming" , IsAdvanced = false});
kent.Courses.Add(new Course { Name = "Appreciatin Single Malts" , IsAdvanced = true});
var refactoringCourse = new Course { Name = "Refactoring" , IsAdvanced = false};
kent.Courses.Add(refactoringCourse);
kent.Courses.Add( new Course { Name = "Brutal Sarcasm" , IsAdvanced = false});
kent.Courses.Remove(refactoringCourse);
}
public void GettingCourseInfo()
{
var kent = new Person();
var advancedCourses = kent.Courses.Count(c => c.IsAdvanced);
}
}
class Person
{
private List<Course> _courses;
// 步驟四:我不想讓使用者看到Courses,設定Private
// 而且不提供直接變更群集,所以把set拿掉
// 並且get改用ReadOnly
public IReadOnlyList<Course> Courses {
get {return _courses.AsReadOnly();}
}
public Person()
{
_courses = new List<Course>();
}
public void AddCourse(Course course){
// 步驟五:修改成_course
_courses.Add(course);
}
public void RemoveCourse(Course course){
// 步驟五:修改成_course
_courses.Remove(course);
}
}
class Course
{
public string Name {get;set;}
public bool IsAdvanced {get;set;}
}
class simple_Example
{
public void ManipulatingCourses()
{
var kent = new Person();
// 步驟五:改用function
kent.AddCourse(new Course { Name = "Smalltalk programming" , IsAdvanced = false});
kent.AddCourse(new Course { Name = "Appreciatin Single Malts" , IsAdvanced = true});
var refactoringCourse = new Course { Name = "Refactoring" , IsAdvanced = false};
// 步驟五:改用function
kent.AddCourse(refactoringCourse);
// 步驟五:改用function
kent.AddCourse( new Course { Name = "Brutal Sarcasm" , IsAdvanced = false});
kent.RemoveCourse(refactoringCourse);
}
public void GettingCourseInfo()
{
var kent = new Person();
var advancedCourses = kent.Courses.Count(c => c.IsAdvanced);
}
}
後繼
封裝群集就如同(206)Encapsulate_Field一樣就不再多說
還是要提醒,只針對有使用或修改的進行開放,其他都都進行封裝,才是對未來有幫助的Code
從設計模式學到重構,真心覺得重構才是對日常工作最有幫助的技能,把程式品質提高才真的是落實盡快下班的最快途徑