Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu

Similar presentations


Presentation on theme: "Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu"— Presentation transcript:

1 Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu
Introduction Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu

2 Design What is a good design? What is a bad design?
What makes one design better or worse than another? Faster Easier to understand Easier to modify Is sometimes a slower executing design better than a faster executing design? Can we objectively measure how good a design is?

3 Why Is Software Complex?
The problem itself is complex. Difficulty of managing the process. Two different teams may not be completely in sync on the way that two pieces of code interact, leading to complexity. The flexibility of software. Creeping featurism. Changing requirements. The world itself changes. What’s wrong with incremental changes? Why not design it right in the first place? “Accidental” vs. essential [Brooks] Accidental: For example, interface not quite right might require an adaptor. Essential: Problem domain is complex.

4 Managing Complexity This course is about managing complexity.
That’s what really distinguishes okay code from great code. What really matters is: How well can you use the language features to manage complexity? Making things simple is not trivial. Strategies to manage complexity? Localize it. This helps lead to the things we think of as a good design. Why does localization help? Complexity is “contained”, so it doesn’t add up across the whole artifact. Practical strategy for localization is modularization. Interfaces between modules. Internal complexity not exposed. How do you start to modularize? Decomposition. Strategies for decomposition? Algorithmic OO Modules are used in all fields of engineering.

5 Code Reuse Why is code reuse good?
How does it help us manage complexity? What’s wrong with copy-and-paste? It concentrates (localizes) complexity. How do we get code reuse? Parameterization is crucial. What is parameterization? In what ways can code be parameterized? Let’s say you are given a mass of code. There is no reuse. What do you do in order to get reuse into that code?

6 How do we get code reuse? i = j*a + j*b; … i = j*a + j*b;
i = f(j, a, b); … i = f(j, a, b); #define xyzzy() i = j*a + j*b xyzzy(); … xyzzy(); #undef xyzzy

7 How do we get code reuse? for (int i = 0; i < 10; i++) { // Code fragment using three // variables x, y, z. // … } // … for (int i = 0; i < 100; i++) { // Same code fragment. // … } loop(10, x, y, z); … loop(100, x, y, z);

8 Consider a case like this. How do we get code reuse?
bool less(int i, int j) { return i < j; } bool greater(int i, int j) { return i > j; } void doit(int op1, int op2, bool (*cmp)(int, int)) { // Fragment A if ((*cmp)(op1, op2)) { // Fragment B } // Fragment C } doit(i, j, &less); doit(k, l, &greater); // Fragment A if (i < j) { // Fragment B } // Fragment C // Fragment A, repeated if (k > l) { // Fragment B, repeated } // Fragment C, repeated Can we avoid having to pass the two variables?

9 Another way: // Fragment A if (i < j) { // Fragment B } // Fragment C // Fragment A, repeated if (k > l) { // Fragment B, repeated } // Fragment C, repeated fragA(); if (i < j) { fragB(); } fragC(); // … fragA(); if (k > l) { fragB(); } fragC();

10 What if the fragments need access to local variables?
Still works? bool less(int i, int j) { return i < j; } bool greater(int i, int j) { return i > j; } void doit(int op1, int op2, bool (*cmp)(int, int)) { // Fragment A i = 2*j + k*l; k = j/2; if ((*cmp)(op1, op2)) { // Fragment B } // Fragment C } doit(i, j, &less); doit(k, l, &greater); // Fragment A i = 2*j + k*l; k = j/2; if (i < j) { // Fragment B } // Fragment C // Fragment A, repeated i = 2*j + k*l; k = j/2; if (k > l) { // Fragment B, repeated } // Fragment C, repeated What if the fragments need access to local variables?

11 Need to pass. bool less(int i, int j) { return i < j; } bool greater(int i, int j) { return i > j; } void doit(int &i, int &j, int &k, int &l, double &x, double &y, int &a, int &b, int &c, int op1, int op2, bool (*cmp)(int, int)) { // Fragment A if ((*cmp)(op1, op2)) { // Fragment B } // Fragment C } // … doit(i, j, k, l, x, y, a, b, c, i, j, less); doit(i, j, k, l, x, y, a, b, c, k, l, greater);

12 Or, in the other solution, looks like:
fragA(&i, j, k, &l); if (i < j) { fragB(a, b, &c); } fragC(&x, &y); // … fragA(&i, j, k, &l); if (k > l) { fragB(a, b, &c); } fragC(&x, &y); Other ideas?

13 Can wrap the parameters in an object using a local struct.
int i, j; double x, y; int a, b, c; struct Helper { Helper(int &i_, int &j_, int &k_, int &l_, double &x_, double &y_, int &a_, int &b_, int &c_) : i(i_), j(j_), k(k_), x(x_), y(x_), a(a_), b(b_), c(b_) {} int &i, &j; double &x, &y; int &a, &b, &c; void fragA() { /* … */ } void fragB() { /* … */ } void fragC() { /* … */ } } h(i, j, k, l, x, y, a, b, c);

14 It’s usually a bit cleaner looking and concise to just put the variables inside the struct.
struct { int i, j, k; double x, y; int a, b, c; void fragA() { /* … */ } void fragB() { /* … */ } void fragC() { /* … */ } } h; h.i = 0; h.j = 0; h.k = 0; h.x = 3.14; h.y = 2.2; h.a = 2; h.b = 3; h.c = 5; h.fragA(); if (h.i < h.j) { h.fragB(); } h.fragC(); // … h.fragA(); if (h.k > h.l) { h.fragB(); } h.fragC();

15 So now, rather than this:
fragA(&i, j, k, &l); if (i < j) { fragB(a, b, &c); } fragC(&x, &y); // … fragA(&i, j, k, &l); if (k > l) { fragB(a, b, &c); } fragC(&x, &y); We have this: h.fragA(); if (i < j) { h.fragB(); } h.fragC(); // … h.fragA(); if (k > l) { h.fragB(); } h.fragC();

16 Rather than passing local variables to helper functions, or storing them in helper objects, we can use a lambda (C++11) to automatically capture them. std::function<bool()> ij_less = [&]() { return i < j; }; std::function<bool()> kl_greater = [&]() { return k > l; }; auto doit = [&](const std::function<bool()> &cmp) { // Fragment A if (cmp()) { // Fragment B } // Fragment C }; doit(ij_less); doit(kl_greater); doit([&]() { return i < j; }); doit([&]() { return k > l; });

17 We can also use lambdas if we want to turn the fragments into functions.
auto fragA = [&]() { /* … */ }; auto fragB = [&]() { /* … */ }; auto fragC = [&]() { /* … */ }; fragA(); if (i < j) { fragB(); } fragC(); fragA(); if (k > l) { frag B(); } fragC();

18 Probably the nicest way is to use lambda’s just for the comparisons:
for (const auto &cmp : std::vector<std::function<bool()>>{ [&]() { return i < j; }, [&]() { return k < l; }, } ) { // Fragment A if (cmp()) { // Fragment B } // Fragment C }

19 We can also just use a macro.
#define xyzzy_doit(op1, op2, cmp) \ /* Fragment A */ \ if (op1 cmp op2 { \ /* Fragment B */ \ } \ /* Fragment C */ xyzzy_doit(i, j, <) xyzzy_doit(k, l, >) Why the funny prefix? This is actually pretty clean looking. What’s the downside?

20 Hierarchy What is it? A ranking of abstractions. Example?
Employee, manager.

21 Examples Example: A hierarchy of living things.
By region? Number of legs? By color? A hierarchy of vehicles. Brand? Number of wheels? Type of engine? Number of axles? A hierarchy for dishes. By country? By style? By content? Often no one best hierarchy, without considering use cases. MI can help resolve conflicting use cases.

22 Purpose of hierarchies
Why? Maximizes other reuse, improves abstractions. Code can be oblivious to the difference. Abstractions and hierarchies go hand-in-hand.

23 Two kinds of hierarchy Consider: An engine is part of a truck. A diesel engine is a kind of engine. First is considered part-of, second is considered is-a. What do they correspond to? Object hierarchies Class hierarchies So, can you have an object hierarchy without a class hierarchy? How about a class hierarchy without an object hierarchy? These two hierarchies are orthogonal.

24 Nature of an Object What is an object, in OOP?
What is or is not an object? red? airplanes? processes? Figuring these out is part of analysis phase. What qualities should objects have? Abstraction Encapsulation Modularity Hierarchy

25 When to Make Things a Separate Class
A checking account: Has a monthly fee. Does not pay interest. A savings account: Has no monthly fee. Pays monthly interest. Should we make these separate classes? If we make them separate classes, then we would still need a base class for code that deals with accounts in general, such as total funds.

26 Could you do OOP before C++/Java?
Design process: Determine objects in the domain Determine operation Determine state Then determine abstraction/encapsulation. Is there a cost to encapsulation? Could you do OOP before C++/Java? Objects are the fundamental building blocks, not algorithms. Could definitely do OOP in C, but the language didn't enforce things, so it is more a matter of style and convention.

27 Summary of Classification/Hierarchies
Given any domain (such as dishes), there are many different ways to classify/organize the entities. In code, we can only have a single class organization. So we have to pick something. MI can be used to alleviate. Other aspects have to implicit, rather than explicit. Or sometimes can be represented in the object hierarchy.

28 Abstraction What is it? Abstraction is usually performed over a set of similar classes. Abstraction is simplifying things. What’s the minimal set of public state and operations that your objects (conforming to the same interface) need to provide to the rest of your code? Principle of least commitment. Abstractions should be minimal.

29 Abstraction is more than just creating a list of operations.
Sometimes there are multiple ways to represent the same thing. What’s the abstraction of a 2-D point? class Point { get_x(); get_y(); set_x(x); set_y(y); }; class Point { get_theta(); get_r(); set_theta(t); set_r(r); } How do we solve this? Represent internally in one of these. Provide all methods. But if we pick the wrong internal representation, then there will be a high conversion cost. Consider these to be two different abstractions. Pick one of them. Some things are “too abstract” to hope for a computer representation that is ideal.

30 Encapsulation What is it?
Hiding the implementation or other details. Is there a difference between abstraction and encapsulation? Can you abstract without encapsulating? Can you encapsulate without abstracting? Flip sides of the same coin.

31 Waterfall Model Requirements Analysis Design Implementation

32 Iterative Development
Give up trying to get the design right from the beginning. Instead, accept that it’s going to take multiple iterations, and try to make streamline that process.

33 Defensive Programming
Most programs are riddled with bugs. Most bugs (like cockroaches) are hidden, unrevealed. When long-hidden bugs show themselves, they are hard to exterminate. Why? Place where bug shows itself is far removed from where bug actually occurs. The bug was actually put into the code a long time ago. Moral: Exterminating bugs is easiest when you catch them in their nest, and when you catch them early. The purpose of defensive programming is to force bugs to show themselves as early as possible, and as close to the origin of the bug as possible. Think about what programmers using your code might do, long after you wrote the code. They may interpret things slightly differently, and use or modify your code in the wrong way. Try to force such bugs to reveal themselves early and quickly.

34 Robustness What does “robust” mean? You can have source robustness.
Portable Likely to catch errors that other programmers do. Is this robust? class String { public: String(const char *s) : str(strdup(s)) { } ~String() { free(str); } private: char *const str; }; You can have run-time robustness. Likely to respond well to abnormal conditions. Example conditions? Need to disallow copy constructor and assignment operator.

35 Let’s say that you are writing a 100 line program.
One of the functions has a potential problem, but you estimate that the chance of it happening is only 1 in 1,000,000 every time the program is run. You figure it’s okay to ignore. Good idea? If there are 10,000 such functions, the probably of failure becomes 1 out of 100.

36 static OSStatus SSLVerifySignedServerKeyExchange(SSLContext
static OSStatus SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams, uint8_t *signature, UInt16 signatureLen) { OSStatus err; if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail; if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail; fail: SSLFreeBuffer(&signedHashes); SSLFreeBuffer(&hashCtx); return err; } This is Apple SSL bug.


Download ppt "Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu"

Similar presentations


Ads by Google