Presentation is loading. Please wait.

Presentation is loading. Please wait.

1 Detailed Class Design CS 236700: Software Design Winter 2004-2005/T7.

Similar presentations


Presentation on theme: "1 Detailed Class Design CS 236700: Software Design Winter 2004-2005/T7."— Presentation transcript:

1 1 Detailed Class Design CS 236700: Software Design Winter 2004-2005/T7

2 2 RNDS: The story so far  Problem description  SRS Use case model  SDD Class design Sequence, Deployment Detailed class design Exception Safety The power of simplicity  Coding Design patterns

3 3 Exception safety: Motivation (1/4) public class EndOfReader extends Exception { } public class Reader { private String s_; private int pos_ = 0; public Reader(String s) { s_ = s; } public char next() throws EndOfReader { if(pos_ == s_.length()) throw new EndOfReader(); return s_.charAt(pos_++); } public class EndOfReader extends Exception { } public class Reader { private String s_; private int pos_ = 0; public Reader(String s) { s_ = s; } public char next() throws EndOfReader { if(pos_ == s_.length()) throw new EndOfReader(); return s_.charAt(pos_++); }  The Reader class allows its client to iterate over a string next() Returns the next available character an EndOfReader exception is thrown when iteration is over

4 4 Exception safety: Motivation (2/4) public class Lifo { public Lifo(int n) { top_ = n; buf_ = new char[n]; } public void push(Reader r) throws EndOfReader { if(top_ == 0) return; top_ -= 1; buf_[top_] = r.next(); } char at(int i) { return buf_[top_ + i]; } private char[] buf_; private int top_; } public class Lifo { public Lifo(int n) { top_ = n; buf_ = new char[n]; } public void push(Reader r) throws EndOfReader { if(top_ == 0) return; top_ -= 1; buf_[top_] = r.next(); } char at(int i) { return buf_[top_ + i]; } private char[] buf_; private int top_; }

5 5 Exception safety: Motivation (3/4) public static void main(String[] args) throws Throwable { Reader r1 = new Reader("ab"); Reader r2 = new Reader(""); Lifo l = new Lifo(2); l.push(r1); try { l.push(r2); } catch(EndOfReader e){ l.push(r1); } System.out.println(“0: “ + l.at(0)); System.out.println(“1: “ + l.at(1)); } public static void main(String[] args) throws Throwable { Reader r1 = new Reader("ab"); Reader r2 = new Reader(""); Lifo l = new Lifo(2); l.push(r1); try { l.push(r2); } catch(EndOfReader e){ l.push(r1); } System.out.println(“0: “ + l.at(0)); System.out.println(“1: “ + l.at(1)); } Output?

6 6 Exception safety: Motivation (4/4)  The previous program produces unpredictable results Lifo.push() does not take into account the implications of an exception being thrown by Reader.next() The value of top_ is decreased before r.next() is called  Lifo.push() is said to be "non exception-safe", i.e: If an exception occurs the object is left is an unstable state public void push(Reader r) throws EndOfReader { if(top_ == 0) return; top_ -= 1; buf_[top_] = r.next(); } public void push(Reader r) throws EndOfReader { if(top_ == 0) return; top_ -= 1; buf_[top_] = r.next(); } If r.next() throws, top_ has already changed its value

7 7 Lifo.push() rewritten  Here's is the correct (exception safe) version of push(): public void push(Reader r) throws EndOfReader { if(top_ == 0) return; buf_[top_ - 1] = r.next(); top_ -= 1; } public void push(Reader r) throws EndOfReader { if(top_ == 0) return; buf_[top_ - 1] = r.next(); top_ -= 1; }

8 8 Exception safety – principles (1/2)  There are several degrees of exception safeness 1.No side effects 2.Side effects do occur, but the object is still useable (the invariants still hold) 3.No Safety  Rules/Approaches for writing exception safe code Catch the exception and then undo all the previous actions Place all throwing actions first, then only non-throwing actions pass-by-pointer/reference instead of pass-by-value Use a non-throwing swap() primitive (especially in C++)

9 9 Exception safety – principles (2/2)  Semi-Generic solution 1.Create a copy of the data structure 2.Execute the request 3.Exception => Start using the copy

10 10 Challenge (Java) The method copy(), below, has two side effects 1.Read a byte from in_ 2.Write a byte to out_ Either of these actions can throw an exception Can you supply an equivalent exception safe code? public class Copier { public FileInputStream in_; public FileOutputStream out_; public void copy() throws IOException { int b = in_.read(); out_.write(b); } public class Copier { public FileInputStream in_; public FileOutputStream out_; public void copy() throws IOException { int b = in_.read(); out_.write(b); }

11 11 Challenge (C++) The function f1(), below, has two side effects 1.prints the value of n 2.Returns the string "abc" Either of these actions can throw an exception Can you supply an equivalent exception safe code? #include using std::string; string f1(int n) { cout << n; return "abc"; } #include using std::string; string f1(int n) { cout << n; return "abc"; }

12 12 The Power of Simplicity  KISS  RAII = Resource Acquisition is Initialization  Samples in C++ The principles are relevant for Java as well. Data members are public to reduce amount of code

13 13 Plain C code FILE* in = fopen(“input.dat”, “r”); if(in == NULL) return; FILE* out = fopen(“output.dat”, “w”); if(out == NULL) { fclose(in); return; } void* buf = malloc(200 * sizeof(char)); if(buf == null) { fclose(in); fclose(out); return; } fread(buf, sizeof(char), 200, in); // Return value ignored fwrite(buf, sizeof(char), 200, out); // - " - free(buf); fclose(out); fclose(in); FILE* in = fopen(“input.dat”, “r”); if(in == NULL) return; FILE* out = fopen(“output.dat”, “w”); if(out == NULL) { fclose(in); return; } void* buf = malloc(200 * sizeof(char)); if(buf == null) { fclose(in); fclose(out); return; } fread(buf, sizeof(char), 200, in); // Return value ignored fwrite(buf, sizeof(char), 200, out); // - " - free(buf); fclose(out); fclose(in);

14 14 C++ Equivalent (1/2) struct File { File(const char* fn, const char* mode) : f_(fopen(fn, mode)) { if(f_ == 0) throw std::exception(“Failure”); } ~File() { fclose(f); } FILE* f_; }; struct Buf { Buf(int n) : b_(new char[n]), size_(n) { } ~Buf() { delete [] b_; } char* b_; int size_; }; struct File { File(const char* fn, const char* mode) : f_(fopen(fn, mode)) { if(f_ == 0) throw std::exception(“Failure”); } ~File() { fclose(f); } FILE* f_; }; struct Buf { Buf(int n) : b_(new char[n]), size_(n) { } ~Buf() { delete [] b_; } char* b_; int size_; };

15 15 C++ Equivalent (2/2) int main() { File in(“input.dat”, “r”); File out(“output.dat”, “w”); Buf buf(200); // Start copying. (Return values are ignored) fread(buf.b_, sizeof(char), 200, in.f_); fwrite(buf.b_, sizeof(char), 200, out.f_); return 0; } int main() { File in(“input.dat”, “r”); File out(“output.dat”, “w”); Buf buf(200); // Start copying. (Return values are ignored) fread(buf.b_, sizeof(char), 200, in.f_); fwrite(buf.b_, sizeof(char), 200, out.f_); return 0; }  This code is less error-prone, cleaner.  Naturally, File,Buf can be expanded to support additional actions

16 16 RAII details  RAII Details The resource ( FILE* ) is managed by an object Constructor => acquires the resource Destructor => releases the resource  Benefits An RAII object is ALWAYS in a useable state The alternative – a possibly empty object Must have an "init" function Has major drawbacks (see following slides) Proper cleanup: No leaks - resource release is seamlessly handled by the compiler. Cleanup code is not affected by exceptions (see following slides)

17 17 A possibly empty object (1/2) // OpenableFile: similar to File, but starts // in an empty state until open() is called // struct OpenableFile() { OpenableFile() : f_(0) { } ~OpenableFile() { if(f_ != 0) fclose(f_); } void open(const char* fn, const char* mode) { if(!empty()) fclose(f_); f_ = fopen(fn, mode); } bool empty() const { return f_ == 0; } FILE* f_; }; // OpenableFile: similar to File, but starts // in an empty state until open() is called // struct OpenableFile() { OpenableFile() : f_(0) { } ~OpenableFile() { if(f_ != 0) fclose(f_); } void open(const char* fn, const char* mode) { if(!empty()) fclose(f_); f_ = fopen(fn, mode); } bool empty() const { return f_ == 0; } FILE* f_; };  A class with an "init" function:

18 18 A possibly empty object (2/3) void f3(OpenableFile& in) { char buf[limit]; if(in.empty()) { // throw exception ? // stop execution ? // return ? // fill buf with some default value ? } else fread(buf, sizeof(char), sizeof(buf), in.f_); } void f3(OpenableFile& in) { char buf[limit]; if(in.empty()) { // throw exception ? // stop execution ? // return ? // fill buf with some default value ? } else fread(buf, sizeof(char), sizeof(buf), in.f_); }  Using OpenableFile :  f3() must be prepared to deal with an “empty” situation  This logic is scattered around the code Reappears in every function that uses OpenableFile objects  Increases complexity

19 19 A possibly empty object (3/3)  Another approach: read() is now a member function of OpenableFile struct OpenableFile() { OpenableFile() : f_(0) { } ~OpenableFile() { if(f_ != 0) fclose(f); } void open(const char* fn, const char* mode) { f_ = fopen(fn, mode); } bool empty() const { return f_ == 0; } int read(Buf& target) { if(empty()) throw std::exception(“Failure”); return fread(target.b_, sizeof(char), target.size_, f_); } FILE* f_; }; struct OpenableFile() { OpenableFile() : f_(0) { } ~OpenableFile() { if(f_ != 0) fclose(f); } void open(const char* fn, const char* mode) { f_ = fopen(fn, mode); } bool empty() const { return f_ == 0; } int read(Buf& target) { if(empty()) throw std::exception(“Failure”); return fread(target.b_, sizeof(char), target.size_, f_); } FILE* f_; };

20 20 RAII vs. Non-RAII  RAII classes Simple Much safer Typically, only the constructor may fail Slightly less useable: Cannot be “empty”  Non RAII classes (= A Possibly empty Object) All key methods must first check the “empty” status Client code must deal with failures  The bottom line: KISS

21 21 The Principles  Tradeoff of functionality and safety  More safety is (usually) achieved by simplifying the state of the class  In JRE 1.4.2 (by Sun): 24.5% of all classes have no state (interface classes, only static fields etc.) 23.6% of all classes do not change their instance fields after construction  Functional languages ??


Download ppt "1 Detailed Class Design CS 236700: Software Design Winter 2004-2005/T7."

Similar presentations


Ads by Google