A set of process, architecture, design, and implementation patterns from a real, large program, the Ptidej Tool Suite. This set shows concrete problems and their solutions in Java. It includes: Be A Profiler, Tests as Documentation, Multi-layered Architecture, Proxy Console, Proxy Disk, Hidden Language, Internal Observer, Run-time Deprecation, String Parsimony, Object Identity, Object Address, Final Construction, StringBuffer as Positioning Element.
Optimizing AI for immediate response in Smart CCTV
Ptidej Architecture, Design, and Implementation in Action v2.1
1. Yann-Gaël Guéhéneuc
(/jan/, he/il)
Work licensed under Creative Commons
BY-NC-SA 4.0 International
Architecture, Design, and
Implementation in Action
yann-gael.gueheneuc@polytmtl.ca
Version 2.1
24/03/29
19. 19/236
Conclusion on the Example
You identified idioms in the given pieces of
source code
These idioms are motifs in a program source
code
These motifs connote a recognized style of
programming
20. 20/236
Motivations
To ease the understanding of a program
architecture to improve its quality
To identify motifs in the program
architecture
30. 30/236
Patterns
Several books, articles
– Amazon.com
• Exclusion
– Unreleased books
– Specific to a technology or frameworks
» e.g., MVVM Unleashed by Michael Brown
– Process oriented, user-interface, programming languages
» e.g., Process Patterns: Building Large-Scale Systems
Using Object Technology by Scott W. Ambler and
Barbara Hanscome
– Proceedings of conferences
– Unrelated to software engineering
31. 31/236
Patterns
1. Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects: Volume 2 (Wiley Software...
by Douglas C. Schmidt, Michael Stal, Hans Rohnert and Frank Buschmann
2. Pattern-Oriented Software Architecture, Patterns for Resource Management: Volume 3 (Wiley Software Patterns
Series... by Michael Kircher and Prashant Jain
3. Pattern-Oriented Software Architecture, A System of Patterns: Volume 1 (Wiley Software Patterns Series) by Frank
Buschmann, Regine Meunier, Hans Rohnert and Peter Sommerlad
4. Pattern-Oriented Software Architecture For Dummies (For Dummies (Computers)) by Robert Hanmer
5. Web Security Patterns by Ramesh Nagappan and Christopher Steel
6. Safe C++ by Vladimir Kushnir
7. Programming in the Large with Design Patterns by Eddie Burris
8. Elemental Design Patterns by Jason McC. Smith
9. Java Application Architecture: Modularity Patterns with Examples Using OSGi (Robert C. Martin Series) by Kirk
Knoernschild
10. Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions (Addison-Wesley
Signature... by Gregor Hohpe and Bobby Woolf
11. Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler)) by Martin Fowler
12. Cognitive Patterns: Problem-Solving Frameworks for Object Technology by Robert K Konitzer, Bobbin Teegarden,
Alexander Rush and Karen M Gardner
13. Service Design Patterns: Fundamental Design Solutions for SOAP/WSDL and RESTful Web Services by Robert
Daigneau
14. The ACE Programmer's Guide: Practical Design Patterns for Network and Systems Programming by Stephen D.
Huston, James CE Johnson and Umar Syyid
15. Patterns for Parallel Software Design (Wiley Software Patterns Series) by Jorge Luis Ortega-Arjona
16. Design Patterns in Object-oriented ABAP by Igor Barbaric
17. Object-Oriented Reengineering Patterns by Oscar Nierstrasz, Stéphane Ducasse and Serge Demeyer
18. Dependency Injection by Dhanji R. Prasanna
19. Object-Oriented Software Engineering Using UML, Patterns, and Java (3rd Edition) by Bernd Bruegge and Allen H.
Dutoit
20. J2EE Design Patterns by William Crawford and Jonathan Kaplan
21. Applying UML and Patterns: An Introduction to Object-oriented Analysis and Design and Iterative Development by
Craig Larman
22. Object-oriented Analysis and Design Using Umlan Introduction to Unified Process and Design Patterns by Mahesh P.
Matha
23. C++ Design Patterns and Derivatives Pricing (Mathematics, Finance and Risk) by M. S. Joshi
24. Effective Java (2nd Edition) by Joshua Bloch
25. Patterns for Fault Tolerant Software (Wiley Software Patterns Series) by Robert Hanmer
26. Implementation Patterns by Kent Beck
27. Patterns for Computer-Mediated Interaction (Wiley Software Patterns Series) by Till Schummer and Stephan Lukosch
28. Pattern Oriented Software Architecture Volume 5: On Patterns and Pattern Languages by Frank Buschmann, Kevlin
Henney and Douglas C. Schmidt
29. Object-Oriented Analysis and Design with Applications (3rd Edition) by Grady Booch, Robert A. Maksimchuk, Michael
W. Engle and Bobbi J. Young
30. Head First Object-Oriented Analysis and Design by Brett D. McLaughlin, Gary Pollice and Dave West
31. Agile Principles, Patterns, and Practices in C# by Robert C. Martin and Micah Martin
32. Design Patterns For Dummies by Steve Holzner
33. Pattern Languages of Program Design 5 by Dragos Manolescu, Markus Voelter and James Noble
34. Design Patterns in Java(TM) (Software Patterns Series) by Steven John Metsker and William C. Wake
35. Object-Oriented Design and Patterns by Cay S. Horstmann
37. Object-Oriented Modeling and Design with UML (2nd Edition) by Michael R. Blaha and James R Rumbaugh
38. Remoting Patterns: Foundations of Enterprise, Internet and Realtime Distributed Object Middleware (Wiley
Software... by Markus Völter, Michael Kircher and Uwe Zdun
39. Software Factories: Assembling Applications with Patterns, Models, Frameworks, and Tools (Wiley Application
Development... by Jack Greenfield, Keith Short, Steve Cook and Stuart Kent
40. Refactoring to Patterns by Joshua Kerievsky
41. Architecting Enterprise Solutions: Patterns for High-Capability Internet-based Systems (Wiley Software Patterns...
by Paul Dyson and Andrew Longshaw
42. Enterprise Patterns and MDA: Building Better Software with Archetype Patterns and UML by Jim Arlow and Ila
Neustadt
43. Data Access Patterns: Database Interactions in Object-Oriented Applications by Clifton Nock
44. Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
45. Pattern-Oriented Analysis and Design: Composing Patterns to Design Software Systems by Sherif M. Yacoub,
Hany H. Ammar, Sherif Yacoub and Hany Ammar
46. Java Extreme Programming Cookbook by Eric M. Burke and Brian M. Coyner
47. J2EE Best Practices: Java Design Patterns, Automation, and Performance (Wiley Application Development
Series) by Darren Broemmer
48. Real-Time Design Patterns: Robust Scalable Architecture for Real-Time Systems by Bruce Powel Douglass
49. Design Patterns Java¿ Workbook by Steven John Metsker
50. EJB Design Patterns: Advanced Patterns, Processes, and Idioms by Floyd Marinescu
51. Streamlined Object Modeling: Patterns, Rules, and Implementation by Jill Nicola, Mark Mayfield and Mike Abney
52. Design Patterns Explained: A New Perspective on Object-Oriented Design by Alan Shalloway and James Trott
53. Small Memory Software: Patterns for systems with limited memory (Software Patterns Series) by James Noble
and Charles Weir
54. AntiPatterns in Project Management by William J. Brown, Hays W. "Skip" McCormick III and Scott W. Thomas
55. Pattern Languages of Program Design 4 (Software Patterns Series) by Brian Foote, Neil Harrison and Hans
Rohnert
56. Testing Object-Oriented Systems: Models, Patterns, and Tools by Robert V. Binder
57. Design Patterns and Contracts by Jean-Marc Jezequel, Michel Train and Christine Mingins
58. Object-Oriented Software Development Using Java: Principles, Patterns, and Frameworks (1/e) by Xiaoping Jia
59. Refactoring: Improving the Design of Existing Code by Martin Fowler, Kent Beck, John Brant and William Opdyke
60. More Process Patterns: Delivering Large-Scale Systems Using Object Technology (SIGS: Managing Object
Technology... by Scott W. Ambler
61. Pattern Hatching: Design Patterns Applied by John Vlissides
62. AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis by William J. Brown, Raphael C. Malveau,
Hays W. "Skip" McCormick and Thomas J. Mowbray
63. A Little Java, A Few Patterns (Language, Speech, & Communication) by Matthias Felleisen, Daniel P. Friedman
and Ralph E. Johnson
64. Pattern Languages of Program Design 3 (v. 3) by Robert C. Martin, Dirk Riehle and Frank Buschmann
65. Object Models: Strategies, Patterns, and Applications (2nd Edition) by Peter Coad, David North and Mark Mayfield
66. Analysis Patterns: Reusable Object Models by Martin Fowler
67. Patterns of Software: Tales from the Software Community by Richard P. Gabriel
68. Pattern Languages of Program Design 2 (v. 2) by John Vlissides, James O. Coplien and Norman L. Kerth
69. Software Patterns by James Coplien
70. Software Architecture: Perspectives on an Emerging Discipline by Mary Shaw and David Garlan
71. Adaptive Object-Oriented Software: The Demeter Method with Propagation Patterns: The Demeter Method with
Propagation... by Karl Lieberherr
72. Pattern Languages of Program Design by James O. Coplien and Douglas C. Schmidt
32. 32/236
Patterns
The following is not a substitute to reading
previous books and practicing
The following is complementary to reading
books and practicing
– These patterns that have been “found” the hard
way and that work (or do not work!)
33. 33/236
Patterns
The following is not a substitute to reading
these books and practicing
The following is complementary to reading
books and practicing
– These patterns that have been “found” the hard
way and that work (or do not work!)
34. 34/236
Patterns
General form as for the GoF , also inspired
by Coplien’s form
– Name
– Problem(s)
– Solution
– Consequences
35. 35/236
Patterns
General form as for the GoF, also inspired
by Coplien’s form
– Name
– Problem(s)
– Example(s)
– Solution
– Example(s)
– Consequences
– (Follow-up)
36. 36/236
Patterns
General form as for the GoF, also inspired
by Coplien’s form
– Name
– Problem(s)
– Example(s)
– Solution
– Example(s)
– Consequences
– (Follow-up)
Problem:
Solution:
37. 37/236
Patterns
General form as for the GoF, also inspired
by Coplien’s form
– Not formal
– Room for interpretation
– But…
• UML-like class diagrams
• UML-like sequence diagrams
• Smalltalk / C++ example code
38. 38/236
Problems
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
39. 39/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
40. 40/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
Design motifs and the
PADL meta-model
41. 41/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
Design motifs and the
PADL meta-model
42. 42/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
Design Meta-modelling
Design motifs and the
PADL meta-model
43. 43/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
Design Meta-modelling
Design motifs and the
PADL meta-model
44. 44/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
Design Meta-modelling
Design motifs and the
PADL meta-model
45. 45/236
Our Solution
What motifs and what model for these
motifs?
What model for the program
architecture?
How to perform the identification?
Design Meta-modelling
Design motifs and the
PADL meta-model
47. 47/236
Case Study
“[E]xisting books on design patterns take a
catalog approach, where they show the
individual design patterns in isolation. This
approach is […] flawed, because you can't see
how the design patterns actually function in the
real world. Most programmers learn by looking
at computer programs.”
—Allen Holub in Holub on Patterns: Learning
Design Patterns by Looking at Code
48. 48/236
Ptidej Tool Suite
Context
– Research work started since my Ph.D. thesis
(2000–2003)
– Pattern Trace Identification, Detection, and
Enhancement in Java
• Theories, methods, and tools, to evaluate and to
improve the quality of object-oriented programs by
promoting the use of idioms, design patterns, and
architectural patterns
49. 49/236
Ptidej Tool Suite
Requirements
– Software tools
• Support the analysis of the implementation, design,
and architecture of object-oriented programs
• Support the detection and introduction of patterns at
the code, design, and architectural levels
50. 50/236
Ptidej Tool Suite
Analysis
– Domain concepts
• “quality of object-oriented”
• “promoting the use of idioms, design patterns, and
architectural patterns”
• “analysis of the implementation, design, and
architecture of object-oriented programs”
• “detection and introduction of patterns at the code,
design, and architectural levels”
51. 51/236
Ptidej Tool Suite
Analysis
– Domain concepts
• “quality of object-oriented”
• “promoting the use of idioms, design patterns, and
architectural patterns”
• “analysis of the implementation, design, and
architecture of object-oriented programs”
• “detection and introduction of patterns at the code,
design, and architectural levels”
52. 52/236
Ptidej Tool Suite
Constraint
– Apply patterns/best practices (existing or new)
when developing the tools
• Architectural
• Design
• Implementation
– Self-imposed but sensible
• “Eating your own dog food”
(http://www.urbandictionary.com/define.php?
term=eating%20your%20own%20dog%20food)
53. 53/236
Ptidej Tool Suite
Related work
– Various parsers (target languages)
e.g., GCCXML
– Various meta-model (sometimes implicit)
• Similar to UML meta-model (1997)
• Before OMG KDM (2003)
• Before Eclipse EMF (2004)
• Before OMG MOF (2006)
– Various tools
e.g., Rigi (H. M. Kienle and H. A. Müller: Rigi – An environment
for software reverse engineering, exploration, visualization, and
redocumentation. Sci. Comput. Program. 75(4): 247-263, 2010)
54. 54/236
Ptidej Tool Suite
Related work
↑ Existing
↓ Too low-level
e.g., GCCXML
↓ No “free” parsers
e.g., UML meta-model
↓ Difficult to access / to use / to adapt
e.g., Rigi
55. 55/236
Ptidej Tool Suite
Related work
– Pierre Cointe’s team in 2000 at
École des Mines de Nantes, France
– Fertile ground
• Hervé Albin-Amiot was already developing a meta-
model to describe design patterns
– “Idioms and patterns Java: application to code synthesis and
detection” (Ph.D. in 2003)
– PDL (Pattern Description Language)
78. 78/236
Ptidej Tool Suite
Advantages
– Creators
– BCRs
– Extensibility
– Reliability
(Has been used in many studies)
79. 79/236
Ptidej Tool Suite
Advantages
– Available!
• https://web.soccerlab.polymtl.ca/rptidej/ptidejlab/
Software/Ptidej 5 Workspace
• Username: guestsvn
• Password: g1u2e3s4t5
– See also
• http://www.ptidej.net/material/inanutshell
• http://www.ptidej.net/material/development/
• http://wiki.ptidej.net/doku.php
80. 80/236
Ptidej Tool Suite
Advantages
– Extensible
e.g., C++ creator provide new/modified constituent
• Factory design pattern
• Builder design pattern
– Including for the user interface
81. 81/236
Ptidej Tool Suite
Limits
– Coarse representation
of method statements
– Unsatisfactory support of non-typed, non-object-
oriented programming languages
• (Very) partial support for Smalltalk
82. 82/236
Ptidej Tool Suite
Performance
– Eclipse v3.1
– Google Chrome v1.0.154.53
• Quality of the model under tests
83. 83/236
Ptidej Tool Suite
Academic Usage
– Dozen of empirical studies
e.g., S. Kpodjedo, F. Ricca, P. Galinier, G. Antoniol,
and Y.-G. Guéhéneuc. MADMatch: Many-to-many
Approximate Diagram Matching for Design
Comparison. IEEE TSE, Feburary 2013
– Please see
• http://www.ptidej.net/publications/
84. 84/236
Ptidej Tool Suite
Concrete Usage
– Build a model from some C++ code (1/2)
public static IIdiomLevelModel generateModelFromCppFilesUsingEclipse(
final String aName,
final String aSourceDirectory,
final IModelListener aModelListener) {
ICodeLevelModel codeLevelModel = null;
try {
final ICodeLevelModelCreator creator =
new padl.creator.cppfile.eclipse.CppCreator(aSourceDirectory);
codeLevelModel =
CPPFactoryEclipse.getInstance().createCodeLevelModel(aName);
if (aModelListener != null) {
codeLevelModel.addModelListener(aModelListener);
}
codeLevelModel.create(creator);
}
catch (final CreationException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
...
85. 85/236
Ptidej Tool Suite
Concrete Usage
– Build a model from some C++ code (1/2)
public static IIdiomLevelModel generateModelFromCppFilesUsingEclipse(
final String aName,
final String aSourceDirectory,
final IModelListener aModelListener) {
ICodeLevelModel codeLevelModel = null;
try {
final ICodeLevelModelCreator creator =
new padl.creator.cppfile.eclipse.CppCreator(aSourceDirectory);
codeLevelModel =
CPPFactoryEclipse.getInstance().createCodeLevelModel(aName);
if (aModelListener != null) {
codeLevelModel.addModelListener(aModelListener);
}
codeLevelModel.create(creator);
}
catch (final CreationException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
...
Specialised creator
86. 86/236
Ptidej Tool Suite
Concrete Usage
– Build a model from some C++ code (1/2)
public static IIdiomLevelModel generateModelFromCppFilesUsingEclipse(
final String aName,
final String aSourceDirectory,
final IModelListener aModelListener) {
ICodeLevelModel codeLevelModel = null;
try {
final ICodeLevelModelCreator creator =
new padl.creator.cppfile.eclipse.CppCreator(aSourceDirectory);
codeLevelModel =
CPPFactoryEclipse.getInstance().createCodeLevelModel(aName);
if (aModelListener != null) {
codeLevelModel.addModelListener(aModelListener);
}
codeLevelModel.create(creator);
}
catch (final CreationException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
...
Specialised creator
Specialised factory to
allow new constituents
87. 87/236
Ptidej Tool Suite
Concrete Usage
– Build a model from some C++ code (1/2)
public static IIdiomLevelModel generateModelFromCppFilesUsingEclipse(
final String aName,
final String aSourceDirectory,
final IModelListener aModelListener) {
ICodeLevelModel codeLevelModel = null;
try {
final ICodeLevelModelCreator creator =
new padl.creator.cppfile.eclipse.CppCreator(aSourceDirectory);
codeLevelModel =
CPPFactoryEclipse.getInstance().createCodeLevelModel(aName);
if (aModelListener != null) {
codeLevelModel.addModelListener(aModelListener);
}
codeLevelModel.create(creator);
}
catch (final CreationException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
...
Specialised creator
Specialised factory to
allow new constituents
Builder design pattern
88. 88/236
Ptidej Tool Suite
Concrete Usage
– Build a model from some C++ code (2/2)
public static IIdiomLevelModel generateModelFromCppFilesUsingEclipse(
final String aName,
final String aSourceDirectory,
final IModelListener aModelListener) {
...
IIdiomLevelModel idiomLevelModel = null;
try {
idiomLevelModel =
(IIdiomLevelModel) new AACRelationshipsAnalysis().invoke(aCodeLevelModel);
}
catch (final UnsupportedSourceModelException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
return idiomLevelModel;
}
89. 89/236
Ptidej Tool Suite
Concrete Usage
– Build a model from some C++ code (2/2)
public static IIdiomLevelModel generateModelFromCppFilesUsingEclipse(
final String aName,
final String aSourceDirectory,
final IModelListener aModelListener) {
...
IIdiomLevelModel idiomLevelModel = null;
try {
idiomLevelModel =
(IIdiomLevelModel) new AACRelationshipsAnalysis().invoke(aCodeLevelModel);
}
catch (final UnsupportedSourceModelException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
return idiomLevelModel;
}
Promotion of the model
90. 90/236
Ptidej Tool Suite
Concrete Usage
– Identify occurrence of the Composite DM
final Problem problem =
CompositeMotif.getProblem(Manager.build(idiomLevelModel));
final StringWriter writer = new StringWriter();
problem.setWriter(new PrintWriter(writer));
problem.automaticSolve(true);
final Properties properties = new Properties();
properties.load(new ReaderInputStream(new StringReader(writer.getBuffer().toString())));
final OccurrenceBuilder solutionBuilder = OccurrenceBuilder.getInstance();
final Occurrence[] solutions = solutionBuilder.getCanonicalOccurrences(properties);
91. 91/236
Ptidej Tool Suite
Concrete Usage
– Identify occurrence of the Composite DM
final Problem problem =
CompositeMotif.getProblem(Manager.build(idiomLevelModel));
final StringWriter writer = new StringWriter();
problem.setWriter(new PrintWriter(writer));
problem.automaticSolve(true);
final Properties properties = new Properties();
properties.load(new ReaderInputStream(new StringReader(writer.getBuffer().toString())));
final OccurrenceBuilder solutionBuilder = OccurrenceBuilder.getInstance();
final Occurrence[] solutions = solutionBuilder.getCanonicalOccurrences(properties);
From a library of
design motifs
92. 92/236
Ptidej Tool Suite
Concrete Usage
– Identify occurrence of the Blob anti-pattern
final IDesignSmellDetection detection = (IDesignSmellDetection) new BlobDetection();
detection.setMetricsFileRepository(ClassFileRepository.getInstance(Repository.class));
detection.setModel(idiomLevelModel);
detection.performDetection();
final String path = ...;
detection.output(new PrintWriter(ProxyDisk.getInstance().fileTempOutput(path)));
final Properties properties = new Properties();
properties.load(new ReaderInputStream(ProxyDisk.getInstance().fileTempInput(path)));
final OccurrenceBuilder solutionBuilder = OccurrenceBuilder.getInstance();
final Occurrence[] solutions = solutionBuilder.getCanonicalOccurrences(properties);
93. 93/236
Ptidej Tool Suite
Concrete Usage
– Identify occurrence of the Blob anti-pattern
final IDesignSmellDetection detection = (IDesignSmellDetection) new BlobDetection();
detection.setMetricsFileRepository(ClassFileRepository.getInstance(Repository.class));
detection.setModel(idiomLevelModel);
detection.performDetection();
final String path = ...;
detection.output(new PrintWriter(ProxyDisk.getInstance().fileTempOutput(path)));
final Properties properties = new Properties();
properties.load(new ReaderInputStream(ProxyDisk.getInstance().fileTempInput(path)));
final OccurrenceBuilder solutionBuilder = OccurrenceBuilder.getInstance();
final Occurrence[] solutions = solutionBuilder.getCanonicalOccurrences(properties);
From a library of
anti-patterns
94. 94/236
Ptidej Tool Suite
Limitations and future work
– Treatment and modelling of method bodies
• Coarse grain multi-language support for method
bodies currently available
• Fine grain through meta-model extension
– Treatment and modelling of non-typed, non-
object-oriented programming languages
• Unsatisfactory support for Smalltalk
• No support for PHP, JavaScript
95. 95/236
Ptidej Tool Suite
Limitations and future work
– Treatment and modelling of method bodies
• Coarse grain multi-language support for method
bodies currently available
• Fine grain through meta-model extension
– Treatment and modelling of non-typed, non-
object-oriented programming languages
• Unsatisfactory support for Smalltalk
• No support for PHP, JavaScript
99. 99/236
Be a Profiler
Name
– Be a profiler
Problems
– Computation time
– Memory consumption
– Easy to “optimise” in the wrong places
• Make code needlessly complex
• Do not improve performances
100. 100/236
Be a Profiler
“Premature optimization is the root of all evil”
—Donald Knuth in Structured Programming
with Go To Statements, December 1974
104. 104/236
Be a Profiler
Solution
– Use a profiler to understand exactly what is
happening and where it is happening
– Profile regularly, as part of the testing phase
– Modify the code to remove only obvious and
dramatic bottlenecks, one at a time
• Time
• Memory
105. 105/236
Be a Profiler
Solution
– Follow the findings and recommendations of
studies on the performance of class-libraries
e.g., Nick Mitchell and Gary Sevitsky. The Causes of
Bloat, the Limits of Health, in the Proceedings of the
22nd ACM OOPSLA conference, October 2007
106. 106/236
Be a Profiler
Example
– Follow the findings and recommendations of
studies on the performance of class-libraries
e.g., Nick Mitchell and Gary Sevitsky. The Causes of
Bloat, the Limits of Health, in the Proceedings of the
22nd ACM OOPSLA conference, October 2007
– From
– To
107. 107/236
Be a Profiler
Consequences
↑ Improved performances
↑ Faster write-run-debug cycles
↔ Contradictory with “If it ain’t broke, don’t fix it”
↓ None, really!
108. 108/236
Tests as Documentation
Name
– Tests as documentation
Problems
– When meeting a large program/framework for
the first time, where to start?
• main(…) methods may not exist or be helpful…
Ptidej include 423 such methods!
• Documentation is often inexistent
112. 112/236
Tests as Documentation
“My advice here is to always provide a way to do
all [documentation] easily with a programmatic
interface, and then treat a [documentation] file as
an optional feature.”
—Martin Fowler in Inversion of Control Containers
and the Dependency Injection pattern
(http://www.martinfowler.com/articles/injection.html)
113. 113/236
Tests as Documentation
“My advice here is to always provide a way to do
all [documentation] easily with a programmatic
interface, and then treat a [documentation] file as
an optional feature.”
—Martin Fowler in Inversion of Control Containers
and the Dependency Injection pattern
(http://www.martinfowler.com/articles/injection.html)
Fowler talked about
configuration originally
114. 114/236
Tests as Documentation
Solution
– Always pair a “development” project with a “test”
project, which contains unit/integration tests
– Use JUnit extensively to ease running
• Avoid main(…) methods
– Write the tests before writing the code
115. 115/236
Tests as Documentation
Example
public class TestPath extends TestCase {
...
public void setUp() {
try {
final char[] entityName = "A".toCharArray();
final IFirstClassEntity entity = Factory.getInstance().createClass(entityName);
final IMethod aGetter = Factory.getInstance().createMethod("get".toCharArray());
aGetter.setReturnType(entityName);
final IParameter aParameter2 = Factory.getInstance().createParameter(
entity, "a".toCharArray(), Constants.CARDINALITY_ONE);
...
entity.addConstituent(aGetter);
entity.addConstituent(aSetter);
entity.addConstituent(aField);
final IPackage aPackage = Factory.getInstance().createPackage("p".toCharArray());
aPackage.addConstituent(entity);
final ICodeLevelModel aCodeLevelModel = Factory.getInstance().createCodeLevelModel("Model");
aCodeLevelModel.addConstituent(aPackage);
116. 116/236
Tests as Documentation
Example
public class TestPath extends TestCase {
...
public void setUp() {
try {
final char[] entityName = "A".toCharArray();
final IFirstClassEntity entity = Factory.getInstance().createClass(entityName);
final IMethod aGetter = Factory.getInstance().createMethod("get".toCharArray());
aGetter.setReturnType(entityName);
final IParameter aParameter2 = Factory.getInstance().createParameter(
entity, "a".toCharArray(), Constants.CARDINALITY_ONE);
...
entity.addConstituent(aGetter);
entity.addConstituent(aSetter);
entity.addConstituent(aField);
final IPackage aPackage = Factory.getInstance().createPackage("p".toCharArray());
aPackage.addConstituent(entity);
final ICodeLevelModel aCodeLevelModel = Factory.getInstance().createCodeLevelModel("Model");
aCodeLevelModel.addConstituent(aPackage);
Factory and API
117. 117/236
Tests as Documentation
Consequences
↑ Even after years (or even when written by
students ), what the code does and how it
does is still evident
↑ Even after years, the code still runs
• Or the tests fail
↑ Novices can copy/paste/modify existing tests to
fit their needs and purposes
↓ None, really!
119. 119/236
Multi-layered Architecture
Name
– Layered architecture
Problems
– Organise your code into manageable chunks
that can be developed “independently”
– Organise your code into identifiable chunks
– Organise your code into controllable chunks
120. 120/236
Multi-layered Architecture
Name
– Layered architecture
Problems
– Organise your code into manageable chunks
that can be developed “independently”
– Organise your code into identifiable chunks
– Organise your code into controllable chunks
124. 124/236
Multi-layered Architecture
Solution
– Divide your code into “projects”
• Eclipse or NetBeans or… projects
– Give names that order the projects along the
lines of the chosen architecture
– Layered architecture
• Ease development
• Ease run-time… even on multiple computers
128. 128/236
Proxy Console
Name
– Proxy console
Problem
– Control output and prepare for production,
when output must be redirected to one or
more files/Console…
129. 129/236
Proxy Console
Solution
– Implement a class to act as a proxy between the
standard outputs and your code
– Implement a class to act as “demultiplexer” to
broadcast output to different channels
130. 130/236
Proxy Console
Examples
– Unified output
– Targets of the output
– Multiple outputs
ProxyConsole.getInstance().setDebugOutput(new PrintWriter(new NullWriter()));
ProxyConsole.getInstance().setErrorOutput(new PrintWriter(Console.getInstance().getErrorWriter()));
ProxyConsole.getInstance().setNormalOutput(new PrintWriter(Console.getInstance().getNormalWriter()));
...
}
catch (final Throwable t) {
t.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
final FileOutputStream logStream = new FileOutputStream("Results.log");
final MultiChannelOutputStream outStream = new MultiChannelOutputStream(System.out, logStream);
System.setOut(new PrintStream(outStream));
131. 131/236
public class ProxyConsole {
private static ProxyConsole UniqueInstance;
public static ProxyConsole getInstance() {
if (ProxyConsole.UniqueInstance == null) {
ProxyConsole.UniqueInstance = new ProxyConsole ();
}
return ProxyConsole.UniqueInstance;
}
private PrintWriter debugOutput = new PrintWriter(new NullWriter());
private PrintWriter errorOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.err));
private PrintWriter normalOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.out));
private ProxyConsole() {
}
public PrintWriter debugOutput() {
return this.debugOutput;
}
public PrintWriter errorOutput() {
return this.errorOutput;
}
public PrintWriter normalOutput() {
return this.normalOutput;
}
public void setDebugOutput(final PrintWriter messageWriter) {
this.debugOutput = messageWriter;
}
public void setErrorOutput(final PrintWriter messageWriter) {
this.errorOutput = messageWriter;
}
public void setNormalOutput(final PrintWriter messageWriter) {
this.normalOutput = messageWriter;
}
}
132. 132/236
public class ProxyConsole {
private static ProxyConsole UniqueInstance;
public static ProxyConsole getInstance() {
if (ProxyConsole.UniqueInstance == null) {
ProxyConsole.UniqueInstance = new ProxyConsole ();
}
return ProxyConsole.UniqueInstance;
}
private PrintWriter debugOutput = new PrintWriter(new NullWriter());
private PrintWriter errorOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.err));
private PrintWriter normalOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.out));
private ProxyConsole() {
}
public PrintWriter debugOutput() {
return this.debugOutput;
}
public PrintWriter errorOutput() {
return this.errorOutput;
}
public PrintWriter normalOutput() {
return this.normalOutput;
}
public void setDebugOutput(final PrintWriter messageWriter) {
this.debugOutput = messageWriter;
}
public void setErrorOutput(final PrintWriter messageWriter) {
this.errorOutput = messageWriter;
}
public void setNormalOutput(final PrintWriter messageWriter) {
this.normalOutput = messageWriter;
}
}
Thread unsafe
133. 133/236
public class ProxyConsole {
private static ProxyConsole UniqueInstance;
public static ProxyConsole getInstance() {
if (ProxyConsole.UniqueInstance == null) {
ProxyConsole.UniqueInstance = new ProxyConsole ();
}
return ProxyConsole.UniqueInstance;
}
private PrintWriter debugOutput = new PrintWriter(new NullWriter());
private PrintWriter errorOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.err));
private PrintWriter normalOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.out));
private ProxyConsole() {
}
public PrintWriter debugOutput() {
return this.debugOutput;
}
public PrintWriter errorOutput() {
return this.errorOutput;
}
public PrintWriter normalOutput() {
return this.normalOutput;
}
public void setDebugOutput(final PrintWriter messageWriter) {
this.debugOutput = messageWriter;
}
public void setErrorOutput(final PrintWriter messageWriter) {
this.errorOutput = messageWriter;
}
public void setNormalOutput(final PrintWriter messageWriter) {
this.normalOutput = messageWriter;
}
}
Thread unsafe
By default, no debug
134. 134/236
public class ProxyConsole {
private static ProxyConsole UniqueInstance;
public static ProxyConsole getInstance() {
if (ProxyConsole.UniqueInstance == null) {
ProxyConsole.UniqueInstance = new ProxyConsole ();
}
return ProxyConsole.UniqueInstance;
}
private PrintWriter debugOutput = new PrintWriter(new NullWriter());
private PrintWriter errorOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.err));
private PrintWriter normalOutput = new AutoFlushPrintWriter(new OutputStreamWriter(System.out));
private ProxyConsole() {
}
public PrintWriter debugOutput() {
return this.debugOutput;
}
public PrintWriter errorOutput() {
return this.errorOutput;
}
public PrintWriter normalOutput() {
return this.normalOutput;
}
public void setDebugOutput(final PrintWriter messageWriter) {
this.debugOutput = messageWriter;
}
public void setErrorOutput(final PrintWriter messageWriter) {
this.errorOutput = messageWriter;
}
public void setNormalOutput(final PrintWriter messageWriter) {
this.normalOutput = messageWriter;
}
}
Thread unsafe
By default, no debug
Makes sure to flush
135. 135/236
Proxy Console
Solution
– Demultiplexer
public class MultiChannelOutputStream extends OutputStream {
private final OutputStream firstStream;
private final OutputStream secondStream;
public MultiChannelOutputStream(
final OutputStream theFirstOutputStream,
final OutputStream theSecondOutputStream) {
this.firstStream = theFirstOutputStream;
this.secondStream = theSecondOutputStream;
}
public void write(final int b) throws IOException {
this.firstStream.write(b);
this.secondStream.write(b);
}
}
136. 136/236
Proxy Console
Example
final FileOutputStream logStream = new FileOutputStream("ConstraintResults.log");
final MultiChannelOutputStream outStream =
new MultiChannelOutputStream(
ProxyConsole.getInstance().normalOutput(),
logStream);
System.setOut(new PrintStream(outStream));
final MultiChannelOutputStream errStream =
new MultiChannelOutputStream(
ProxyConsole.getInstance().normalOutput(),
logStream);
System.setErr(new PrintStream(errStream));
139. 139/236
Proxy Console
Consequences
↑ More professional code
• Output to Console during development
• Output to a file (and–or whatever) in production
– No output to the Console to “hide” any unexpected behavior,
forgotten debug messages…
• Introduce novel channels, i.e., warningOutput()
↓ Tiny impact on performance
↓ Coupling to ProxyConsole and its project
140. 140/236
Proxy Disk
Name
– Proxy disk
Problems
– Need to write temporary files to disk
– Need to access data from disk
• Normal behaviour
• Tests
141. 141/236
Proxy Disk
Example
– Scattered in your code
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
142. 142/236
Proxy Disk
Example
– Scattered in your code
– With many variants…
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
143. 143/236
Proxy Disk
Example
– Scattered in your code
– With many variants…
• Which may or may not work depending on the order
of method executions
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
144. 144/236
Proxy Disk
Example
– One possible variant
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
145. 145/236
Proxy Disk
Example
– One possible variant
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
What if aName does not
end with a file separator?
146. 146/236
Proxy Disk
Example
– Another possible variant
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
147. 147/236
Proxy Disk
Example
– Another possible variant
final String newOutputDirectoryName = anOutputDirectoryName + aName + File.separatorChar;
final File newOutputDirectiory = new File(newOutputDirectoryName);
if (!newOutputDirectiory.exists()) {
newOutputDirectiory.mkdirs();
}
this.analyseCodeLevelModel(someSmells, aName, annotatedCodeLevelModel2, newOutputDirectoryName);
What if the directory
does not/already exist?
148. 148/236
Proxy Disk
Solution
– Have a single object to manage file access
– Centralise access
• Files
• Directories (always ending with a file separator)
– Manage the creation of directories (if needed)
– Handle
• FileReader
• FileWriter
• File/String
149. 149/236
Proxy Disk
Solution
– Have a single object to manage file access
– Centralise access
• Files
• Directories (always ending with a file separator)
– Manage the creation of directories (if needed)
– Handle
• FileReader
• FileWriter
• File/String
In Java, can describe
a file or a directory
150. 150/236
Proxy Disk
Example
– Single access point
final String path = anOutputDirectory + "DetectionResults in " + aName +
" for " + antipatternName + ".ini";
detection.output(new PrintWriter(ProxyDisk.getInstance().fileTempOutput(path)));
Takes care of
the “details”
151. 151/236
Proxy Disk
Solution
public class ProxyDisk {
private static final String TEMP_DIRECTORY = "../Temp/";
private static ProxyDisk UniqueInstance;
public static ProxyDisk getInstance() {
if (ProxyDisk.UniqueInstance == null) {
ProxyDisk.UniqueInstance = new ProxyDisk();
}
return ProxyDisk.UniqueInstance;
}
private File tempDirectory;
private ProxyDisk() {
try {
this.tempDirectory =
this.createDirectories(ProxyDisk.TEMP_DIRECTORY);
}
catch (final IOException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
}
...
By default, we create
a temporary directory
152. 152/236
Proxy Disk
Solution
private File createDirectories(final String aPathToADirectory)
throws IOException {
final File defaultDirectory = new File(aPathToADirectory);
if (!defaultDirectory.exists()) {
if (!defaultDirectory.mkdir()) {
throw new IOException(
"FileOutputProxy cannot create the necessary directory: "
+ aPathToADirectory);
}
}
else if (defaultDirectory.isFile()) {
throw new IOException(
"FileOutputProxy cannot use a file as a directory: "
+ aPathToADirectory);
}
return defaultDirectory;
}
Creates as many
directories as needed
153. 153/236
Proxy Disk
private File createFile(final String aFileName, final boolean shouldAppend) {
File file;
int indexOfLastPathSeparator;
if ((indexOfLastPathSeparator =
Math.max(aFileName.lastIndexOf('/'), aFileName.lastIndexOf(''))) > -1) {
indexOfLastPathSeparator++;
final String somePaths = aFileName.substring(0, indexOfLastPathSeparator);
final String fileName = aFileName.substring(indexOfLastPathSeparator);
try {
file = new File(this.createDirectories(somePaths), fileName);
}
catch (final IOException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
ProxyConsole.getInstance().errorOutput().println(
"FileOutputProxy will use its default directory");
file = new File(ProxyDisk.TEMP_DIRECTORY, fileName);
}
}
else {
file = new File(aFileName);
}
if (file.exists() && !shouldAppend) {
ProxyConsole.getInstance().warningOutput().println(
"ProxyDisk reports that the file already exists: “ + aFileName);
}
return file;
}
154. 154/236
Proxy Disk
private File createFile(final String aFileName, final boolean shouldAppend) {
File file;
int indexOfLastPathSeparator;
if ((indexOfLastPathSeparator =
Math.max(aFileName.lastIndexOf('/'), aFileName.lastIndexOf(''))) > -1) {
indexOfLastPathSeparator++;
final String somePaths = aFileName.substring(0, indexOfLastPathSeparator);
final String fileName = aFileName.substring(indexOfLastPathSeparator);
try {
file = new File(this.createDirectories(somePaths), fileName);
}
catch (final IOException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
ProxyConsole.getInstance().errorOutput().println(
"FileOutputProxy will use its default directory");
file = new File(ProxyDisk.TEMP_DIRECTORY, fileName);
}
}
else {
file = new File(aFileName);
}
if (file.exists() && !shouldAppend) {
ProxyConsole.getInstance().warningOutput().println(
"ProxyDisk reports that the file already exists: “ + aFileName);
}
return file;
}
Creates as many
directories as needed
155. 155/236
Proxy Disk
private File createFile(final String aFileName, final boolean shouldAppend) {
File file;
int indexOfLastPathSeparator;
if ((indexOfLastPathSeparator =
Math.max(aFileName.lastIndexOf('/'), aFileName.lastIndexOf(''))) > -1) {
indexOfLastPathSeparator++;
final String somePaths = aFileName.substring(0, indexOfLastPathSeparator);
final String fileName = aFileName.substring(indexOfLastPathSeparator);
try {
file = new File(this.createDirectories(somePaths), fileName);
}
catch (final IOException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
ProxyConsole.getInstance().errorOutput().println(
"FileOutputProxy will use its default directory");
file = new File(ProxyDisk.TEMP_DIRECTORY, fileName);
}
}
else {
file = new File(aFileName);
}
if (file.exists() && !shouldAppend) {
ProxyConsole.getInstance().warningOutput().println(
"ProxyDisk reports that the file already exists: “ + aFileName);
}
return file;
}
Creates as many
directories as needed
Fails safe
156. 156/236
Proxy Disk
private File createFile(final String aFileName, final boolean shouldAppend) {
File file;
int indexOfLastPathSeparator;
if ((indexOfLastPathSeparator =
Math.max(aFileName.lastIndexOf('/'), aFileName.lastIndexOf(''))) > -1) {
indexOfLastPathSeparator++;
final String somePaths = aFileName.substring(0, indexOfLastPathSeparator);
final String fileName = aFileName.substring(indexOfLastPathSeparator);
try {
file = new File(this.createDirectories(somePaths), fileName);
}
catch (final IOException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
ProxyConsole.getInstance().errorOutput().println(
"FileOutputProxy will use its default directory");
file = new File(ProxyDisk.TEMP_DIRECTORY, fileName);
}
}
else {
file = new File(aFileName);
}
if (file.exists() && !shouldAppend) {
ProxyConsole.getInstance().warningOutput().println(
"ProxyDisk reports that the file already exists: “ + aFileName);
}
return file;
}
Creates as many
directories as needed
Fails safe
Friendly warning
162. 162/236
Proxy Disk
Examples
final XStream xstream = new XStream();
xstream.fromXML(
ProxyDisk.getInstance().fileAbsoluteInput(
Utils.TEMPORARY_OUTPUT_FILE),
aCodeLevelModel);
final PrintWriter writer =
new PrintWriter(ProxyDisk.getInstance().fileAbsoluteOutput(
Constants.PROGRAM_FILE_NAME + ".log",
true));
detection.output(new PrintWriter(ProxyDisk.getInstance().fileTempOutput(path)));
final String commandLine =
('"' + Files.getClassPath(OccurrenceGenerator.class)
+ PropertyManager.getSolverDirectory()
+ PropertyManager.getSolverCommand() + "" "
+ PropertyManager.getSolverArguments() + " -f ""
+ System.getProperty("user.dir") + '/'
+ ProxyDisk.getInstance().directoryTempName() + "Instructions.cl"")
163. 163/236
Proxy Disk
Consequences
↑ Systematic, tested, consistent access to files
↑ Ease debugging with a single access point
↔ Thread safety
• Fails-safe / fails-fast
↔ Default encoding
↓ Possible bottleneck
• But possibility of caching
164. 164/236
Proxy Disk
Follow-up
– Same pattern for database accesses
• Check for credentials, SQL injection
– Same pattern for URL accesses
• Could be combined with ProxyDisk
165. 165/236
Hidden Language
Name
– Hidden language
Problems
– Manipulate, pass around, data coming from
third-party library and provided as Strings
166. 166/236
Hidden Language
Problems (cont’d)
– Also noted in Design Patterns for Teaching Type
Checking in a Compiler Construction Course by
Ortin, Zapico, and Cueva)
“The most serious disadvantage of [a string] is
the need to process the string as a program […].
[U]sing object-orientation, […] common and
specific [methods] can be properly set.”
171. 171/236
Hidden Language
Solution
– Use Strings
– Of course…
… Again, this obvious solution is not the better
• No semantics
• No compile-time checks
172. 172/236
Hidden Language
Example 1
– Class padl.creator.classfile.
relationship.DeepMethodInvocation
Analyzer
– Variable listOfCouples containing, e.g.,
[null,java.lang.Object<init>.void()]
173. 173/236
Hidden Language
Example 1
– Class padl.creator.classfile.
relationship.DeepMethodInvocation
Analyzer
– Variable listOfCouples containing, e.g.,
[null,java.lang.Object<init>.void()]
What do you
make of this?
174. 174/236
Hidden Language
Example 2
– Handling textual data
1.100.ClassGlobalVariable-0 = org.apache.html.dom.HTMLDocumentImpl
1.100.ClassGlobalVariable-0.FieldName-0 = class$0
1.100.ClassGlobalVariable-0.FieldName-1 = class$1
…
176. 176/236
Hidden Language
As an aside…
– A meta-model defines a language to express
and reason about the existence,
• interface IMethodInvocation
properties,
• IMethodInvocation.getType()
and relations among domain concepts
• IMethodInvocation.getCalledMethod()
180. 180/236
Hidden Language
Consequences
↑ Use and debugging significantly easier
↑ Decoration, serialisation…
↑ Compile-time verification
↓ Memory overhead due to objects’ overhead
↓ Performance decrease due to indirections
181. 181/236
Internal Observer
Name
– “Internal” observer
Problems
– Limit dependencies among classes and their
instances
– Yet, update some instances when other
instances are added/removed/changed
182. 182/236
Internal Observer
Solution
– Implement the Observer design pattern
– Use the Observer design pattern internally to
keep consistency in your model resorting to
difficult logic
183. 183/236
Internal Observer
Example
– Definition
– Use
public interface IObservable {
void addModelListener(final IModelListener aModelListener);
void addModelListeners(final List aListOfModelListeners);
void fireModelChange(final String anEventType, final IEvent anEvent);
Iterator getIteratorOnModelListeners();
void removeModelListener(final IModelListener aModelListener);
void removeModelListeners(final List modelListeners);
}
public interface IAbstractModel extends IContainer, INavigable, IObservable {
...
184. 184/236
Internal Observer
Example
private class TopLevelEntityManager implements IModelListener, Serializable {
...
public void entityRemoved(final EntityEvent entityEvent) {
final IConstituentOfModel constituent = entityEvent.getEntity();
if (constituent instanceof IFirstClassEntity) {
....this.removeTopLevelEntityFromID(constituent.getID());
}
}
public void entityAdded(final EntityEvent entityEvent) {
final IConstituentOfModel constituent = entityEvent.getEntity();
if (constituent instanceof IFirstClassEntity && !(constituent instanceof IMemberEntity)) {
....this.addTopLevelEntity(constituent);
}
}
….
}
185. 185/236
Internal Observer
Consequences
↑ All the benefits of the Observe design pattern
↑ Assurance to have a consistent model
↑ Remove need for complex updating logic
• Huge simplification
↓ Slight performance decrease
• Due to indirections
186. 186/236
Run-time Deprecation
Name
– Run-time deprecation
Problem
– Let users know that they should not call certain
methods while internal code can call them
– Avoid deprecated annotation that generate
spurious warnings
188. 188/236
Run-time Deprecation
Solution
public final class ConcreteReceiverGuard {
private static ConcreteReceiverGuard UniqueInstance;
public static ConcreteReceiverGuard getInstance() {
if (ConcreteReceiverGuard.UniqueInstance == null) {
ConcreteReceiverGuard.UniqueInstance = new ConcreteReceiverGuard();
}
return ConcreteReceiverGuard.UniqueInstance;
}
private ConcreteReceiverGuard() {
}
...
189. 189/236
Run-time Deprecation
private void doCheck(
final String aConcreteReceiverClassToEnforce,
final String anErrorMessage) {
class ConcreteReceiverGuardThrownException extends RuntimeException {
private static final long serialVersionUID = -4100342857707204144L;
}
try {
throw new ConcreteReceiverGuardThrownException();
}
catch (final ConcreteReceiverGuardThrownException e) {
final StackTraceElement[] stackTrace = e.getStackTrace();
if (stackTrace.length < 4) {
// Error in the call of the guard
}
else {
final StringBuffer buffer = new StringBuffer();
buffer.append(stackTrace[2].getClassName());
buffer.append('.');
buffer.append(stackTrace[2].getMethodName());
final String nameOfGuardedMethod = buffer.toString();
final String nameOfClassCallingGuardedMethod =
stackTrace[3].getClassName();
if (!nameOfClassCallingGuardedMethod
.equals(aConcreteReceiverClassToEnforce)) {
// Runtime deprecation!
}
}
}
}
190. 190/236
Run-time Deprecation
Solution
public void check(
final String aConcreteReceiverClassToEnforce,
final String anErrorMessage) {
// Yann 2013/04/05: Stack!
// I added this spurious method to make sure that the
// doCheck() method is always called with the same stack
// depth from this class, i.e., 2 :-)
this.doCheck(aConcreteReceiverClassToEnforce, anErrorMessage);
}
public void check(
final Class aConcreteReceiverClassToEnforce,
final String anErrorMessage) {
this.doCheck(aConcreteReceiverClassToEnforce.getName(), anErrorMessage);
}
}
191. 191/236
Run-time Deprecation
Example
public void create(final ICodeLevelModelCreator aCodeLevelCreator) throws CreationException {
ConcreteReceiverGuard.getInstance().check(
"padl.generator.helper.ModelGenerator",
"Please use the methods in "padl.generator.helper.ModelGenerator“...");
aCodeLevelCreator.create(this);
}
197. 197/236
String Parsimony
Solution
– Use Strings
– Of course…
… But the most obvious solution is not the better
• To much overhead in memory
• See the article “The Causes of Bloat, the Limits of
Health” by Nick Mitchell and Gary Sevitsky
199. 199/236
String Parsimony
As an aside…
– Ockham's razor is “a principle of parsimony,
economy, or succinctness used in logic and
problem-solving” [Wikipedia]
– Why use more when you can use less?
c. 1287 – 1347
200. 200/236
String Parsimony
Solution
– Use char[]
– To manipulate char[]
• Arrays from Java class library
• ArrayUtils from Apache Commons Lang
• TCharArrayList from Trove4J
201. 201/236
String Parsimony
Example
public final class AnalysisEvent implements IEvent {
private final char[] name;
private final char[] elementType;
/**
* @deprecated by AnalysisEvent(final char[])
*/
public AnalysisEvent(final String aName) {
this.name = aName.toCharArray();
this.elementType = new char[0];
}
/**
* @deprecated by AnalysisEvent(final char[], final char[])
*/
public AnalysisEvent(final char[] aName) {
this.name = aName;
this.elementType = new char[0];
}
public AnalysisEvent(final char[] aName, final char[] anElementType) {
this.name = aName;
this.elementType = anElementType;
}
public char[] getConstituentName() {
return this.name;
}
...
}
202. 202/236
String Parsimony
Consequences
↑ Major gains in terms of space and time
↔ Code not significantly more complicate
• Eclipse warns of “unsafe” use of char[] with
“Must explicitly convert the char[] into a String”
↓ Costly to go from String/String[] to
char[]-related objects
• Use @deprecated
• Encapsulate accesses
203. 203/236
Identity
Name
– Identity
Problems
– Distinguish instances whose values may be
identical
– Make similar instances that can be persisted in
database (serialised)
204. 204/236
Identity
Solution
– Distinguish between
• The value of the fields of an instance
• Its ID in the system at run-time
• Its unique ID automatically given by the VM
– Implement equals(Object)
– Implement hashCode()
205. 205/236
Identity
As an aside…
– An object has typically three identities
• Its System.identityHashCode(Object)
• Its hashCode() (see also equals())
• The hashCode() of its fields (recursively)
which may or may not be different
• See the article “Why Object Serialization is
Inappropriate for Providing Persistence in Java”
by Huw Evans
206. 206/236
Identity
Example
abstract class Constituent implements IConstituent {
private char[] id;
private char[] name;
...
public boolean equals(final Object obj) {
if (!(obj instanceof IConstituent)) {
return false;
}
return Arrays.equals(this.getID(), ((IConstituent) obj).getID());
}
...
public int hashCode() {
return this.getID().hashCode();
}
...
}
207. 207/236
Identity
Example
abstract class Constituent implements IConstituent {
private char[] id;
private char[] name;
...
public boolean equals(final Object obj) {
if (!(obj instanceof IConstituent)) {
return false;
}
return Arrays.equals(this.getID(), ((IConstituent) obj).getID());
}
...
public int hashCode() {
return this.getID().hashCode();
}
...
}
Takes care of null values
208. 208/236
Identity
Example
public class TestDB4OSerialiserSanity extends TestCase {
private IDesignMotifModel originalModel;
private IDesignMotifModel serialisedModel;
...
protected void setUp() throws ModelDeclarationException, CloneNotSupportedException {
this.originalModel = new Composite();
this.serialisedFileName =
DB4OSerialiser.getInstance().serialiseWithAutomaticNaming(this.originalModel);
this.serialisedModel = (IDesignMotifModel)
DB4OSerialiser.getInstance().deserialise(this.serialisedFileName);
}
public void testModels() {
Assert.assertEquals("Models", this.originalModel, this.serialisedModel);
Assert.assertEquals("Model names", this.originalModel.getDisplayName(),
this.serialisedModel.getDisplayName());
Assert.assertEquals("Model IDs", this.originalModel.getDisplayID(),
this.serialisedModel.getDisplayID());
Assert.assertTrue("Identity hashcodes of the models are different",
System.identityHashCode(this.originalModel) !=
System.identityHashCode(this.serialisedModel));
}
}
209. 209/236
Identity
Example
public class TestDB4OSerialiserSanity extends TestCase {
private IDesignMotifModel originalModel;
private IDesignMotifModel serialisedModel;
...
protected void setUp() throws ModelDeclarationException, CloneNotSupportedException {
this.originalModel = new Composite();
this.serialisedFileName =
DB4OSerialiser.getInstance().serialiseWithAutomaticNaming(this.originalModel);
this.serialisedModel = (IDesignMotifModel)
DB4OSerialiser.getInstance().deserialise(this.serialisedFileName);
}
public void testModels() {
Assert.assertEquals("Models", this.originalModel, this.serialisedModel);
Assert.assertEquals("Model names", this.originalModel.getDisplayName(),
this.serialisedModel.getDisplayName());
Assert.assertEquals("Model IDs", this.originalModel.getDisplayID(),
this.serialisedModel.getDisplayID());
Assert.assertTrue("Identity hashcodes of the models are different",
System.identityHashCode(this.originalModel) !=
System.identityHashCode(this.serialisedModel));
}
}
Different JVM identities
211. 211/236
Identity
Pragmatically
– Nowadays, better use builders
• EqualsBuilder and HashCodeBuilder in
Apache Commons Lang
• http://stackoverflow.com/questions/27581/
overriding-equals-and-hashcode-in-java
212. 212/236
Address
Name
– Address
Problems
– When dealing with complex objects, such as
models of object-oriented programs, it is often
necessary to uniquely and directly reference one
particular constituent
213. 213/236
Address
Name
– Address
Problems
– When dealing with complex objects, such as
models of object-oriented programs, it is often
necessary to uniquely and directly reference
one particular constituent
214. 214/236
Address
Example
public void removeTopLevelEntityFromID(final char[] anID) {
try {
final IConstituent constituent =
this.topLevelEntitiesContainer.getTopLevelEntityFromID(anID);
final IContainer container =
Finder.findContainer(constituent.getDisplayPath(), this);
container.removeConstituentFromID(anID);
}
catch (final FormatException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
}
215. 215/236
Address
Example
public void removeTopLevelEntityFromID(final char[] anID) {
try {
final IConstituent constituent =
this.topLevelEntitiesContainer.getTopLevelEntityFromID(anID);
final IContainer container =
Finder.findContainer(constituent.getDisplayPath(), this);
container.removeConstituentFromID(anID);
}
catch (final FormatException e) {
e.printStackTrace(ProxyConsole.getInstance().errorOutput());
}
}
Without back-pointers!
216. 216/236
Address
Solution
– Implement a unique “path” identifier that
describe uniquely and precisely the “address”
of a constituent
public interface INavigable {
String getDisplayPath();
char[] getPath();
}
217. 217/236
Address
Implementation
abstract class Abstract...ContainerOfConstituents extends GenericObservable
implements IContainer, Serializable {
...
private void updatePathUponAddition(final IConstituent aConstituent) {
final char[] containerPath = this.containerConsitituent.getPath();
final char[] constituentPath = aConstituent.getPath();
final char[] newPath = ...
((Constituent) aConstituent).setPath(newPath);
if (aConstituent instanceof IContainer) {
final Iterator iterator = ((IContainer) aConstituent).getIteratorOnConstituents();
while (iterator.hasNext()) {
final Constituent constituent = (Constituent) iterator.next();
this.updatePathUponAddition(constituent);
}
}
}
private void updatePathUponRemoval(final IConstituent aConstituent) {
...
218. 218/236
Address
Implementation
abstract class Abstract...ContainerOfConstituents extends GenericObservable
implements IContainer, Serializable {
...
private void updatePathUponAddition(final IConstituent aConstituent) {
final char[] containerPath = this.containerConsitituent.getPath();
final char[] constituentPath = aConstituent.getPath();
final char[] newPath = ...
((Constituent) aConstituent).setPath(newPath);
if (aConstituent instanceof IContainer) {
final Iterator iterator = ((IContainer) aConstituent).getIteratorOnConstituents();
while (iterator.hasNext()) {
final Constituent constituent = (Constituent) iterator.next();
this.updatePathUponAddition(constituent);
}
}
}
private void updatePathUponRemoval(final IConstituent aConstituent) {
...
This is not a back-pointer,
it is a surrogate for this
219. 219/236
Address
Consequences
↑ Unique, direct access to any constituent
↑ Path can be build/divided programmatically
↓ Slight impact on memory
Related to
– Hidden language
220. 220/236
Final Construction
Name
– Final construction
Problems
– Objects encapsulate state, which should always
be valid (class invariant)
– Providing setter methods raise questions about
which setters to use and when to use them
223. 223/236
Final Construction
“My long running default with objects is as much as
possible, to create valid objects at construction time.
This advice goes back to Kent Beck's Smalltalk Best
Practice Patterns […]. Constructors with parameters give
you a clear statement of what it means to create a valid
object in an obvious place. If there's more than one way
to do it, create multiple constructors that show the different
combinations.”
—Martin Fowler in Inversion of Control Containers
and the Dependency Injection pattern
(http://www.martinfowler.com/articles/injection.html)
224. 224/236
Final Construction
Solution
– Provide explicit constructors
– Limit the number of setters
– Limit the necessity to use setters after the
instantiation of an object to obtain a valid object
227. 227/236
Final Construction
Consequences
↑ When creating an object, no “bad” surprises
↑ Explicit (and compile-time checks) of the objects
necessary to instantiate a class
↔ The state of some objects must change at run-
time and setters cannot be avoided
228. 228/236
StringBuffer As
Positioning Element
Name
– StringBuffer as positioning element
Problems
– Concatenating Strings creates lots of
intermediary objects at runtime
– Concatenated Strings do not look well in IDEs
230. 230/236
StringBuffer As
Positioning Element
Solution
– Use StringBuffers to append Strings and
chars without creating temporary Strings
– Use StringBuffers to obtain a more readable
layout of the code
231. 231/236
StringBuffer As
Positioning Element
final StringBuilder arg = new StringBuilder();
arg.append("-Xmx2048M -classpath "");
arg.append(equinoxLauncherPath);
arg.append("" ");
arg.append("org.eclipse.equinox.launcher.Main");
arg.append(" -application PADL_Creator_Cpp_Eclipse.Launcher");
arg.append(" -data "");
arg.append(this.pathToCurrentWorkspace);
arg.append(Common.EQUINOX_RUNTIME_WORKSPACE);
arg.append(""");
arg.append(" -configuration "file:");
arg.append(this.pathToCurrentWorkspace);
arg.append(".metadata/.plugins/org.eclipse.pde.core/PADL Creator C++ (Eclipse)/"");
arg.append(" -dev "file:");
arg.append(this.pathToCurrentWorkspace);
arg.append(".metadata/.plugins/org.eclipse.pde.core/PADL Creator C++ (Eclipse)/dev.properties" ");
arg.append(architecture);
arg.append(" -consoleLog ");
arg.append(Common.ARGUMENT_DIRECTORY_TARGET_CPP_FILES);
arg.append("="");
arg.append(aRootDirectoryContainingCPPFiles);
arg.append("" ");
arg.append(Common.ARGUMENT_DIRECTORY_PTIDEJ_WORKSPACE);
arg.append("="");
arg.append(this.pathToCurrentWorkspace);
arg.append("" ");
argument.setValue(arg.toString());
234. 234/236
StringBuffer As
Positioning Element
Follow-up
– See http://stackoverflow.com/questions/47605/
java-string-concatenation-concat-vs-operator
– StringBuilder 0ms (longest 16ms)
– concat() 100,00ms (10s)
– a += b 400,00ms (40s)
String c = a;
for (long i = 0; i < 100000L; i++) {
c = c.concat(b);
}