Simon Roberts wrote his first program in 1978 in high school on punched cards. He started his career as a programmer building embedded control systems in a variety of assembly languages, C, and C++. Alongside programming, Simon taught part-time at a local college. In 1994 his career transitioned to full-time teacher and part time programmer, and he joined Sun Microsystems in 1995 where he worked until going independent in 2004
While at Sun he created training courses on a diverse range of Java topics, developed the original Sun Certified Java Programmer and Developer exams, and presented at JavaOne and other conferences on Java and Java performance topics.
Today, Simon is president of Dancing Cloud Services, LLC., based in Westminster Colorado, and he provides training, course development, and mentoring services in Java, Scala, JavaScript, Python, Go, along with design, and software architecture topics. Simon is equally comfortable offering training in classroom, recorded video, and live, remote-access, formats.
For many beginning and intermediate software engineers, design is something of a secret anxiety. Often we know we can create something that works, and we can likely include a design pattern or two tif only to give our proposal some credibility. But sometimes, we're left with a nagging feeling that there might be a better design, or more appropriate pattern, and we might not be really confident that we can justify our choices.
This session investigates the fundamental driving factors behind good design choices so we can balance competing concerns and confidently justify why we did what we did. The approach presented can be applied not only to design, but also to what's often separated out under the term “software architecture”.
Along the journey, we'll use the approach presented to derive several of the well known “Gang of Four” design patterns, and in so doing conclude that they are the product of sound design applied to a context and not an end in themselves.
Course outline
Background: three levels of “design”
Data structure and algorithm
Design
Software Architecture
Why many programmers struggle with design
What makes a design “better” or “worse” than any other?
The pressures of the real world versus a learning environment
A time-honored engineering solution
Identifying the problem
Dissecting the elements
Creating a working whole from the parts
Deriving three core design patterns from principles
Decorator
Strategy
Sidenote, why traditional inheritance is bad
Command or “higher order function”
Setup requirements
This course is largely language agnostic, but does include some live coding demonstrations. Attendees will have access to the code that's created via a git repo. The majority of the examples will work in any version of Java from version 11 onwards. You can use any Java development environment / IDE that you like and no other tooling is required.
Java's Generics syntax provides us with a means to increase the reusability of our code by allowing us to build software, particularly library software, that can work on many different types, even with limited knowledge about those types. The most familiar examples are the classes in Java's core collections API which can store and retrieve data of arbitrary types, without degenerating those types to java.lang.Object.
However, while the generics mechanism is very simple to use in simple cases such as using the collections API, it's much more powerful than that. Frankly, it can also be a little puzzling.
This session investigates the issues of type erasure, assignment compatibility in generic types, co- and contra-variance, through to bridge methods.
Course outline
Type erasure
Two approaches for generics and Java's design choice
How to break generics (and how not to!)
Maintaining concrete type at runtime
Assignment compatibility of generic types
What's the problem–Understanding Liskov substitution in generic types
Co-variance
Two syntax options for co-variance
Contra-variance
Syntax for contra-variance
Worked examples with co- and contra-variance
Building arrays from generic types
Effective use of functional interfaces
Bridge methods
Review of overloading requirements
Faking overloading in generic types
Setup requirements
This course includes extensive live coding demonstrations and attendees will have access to the code that's created via a git repo. The majority of the examples will work in any version of Java from version 11 onwards, but some might use newer library features. You can use any Java development environment / IDE that you like and no other tooling is required.
One of the features that distinguished Java from a majority of mainstream languages at the time it was released was that it includes a platform independent threading model.
The Java programming language provides core, low-level, features to control how threads interact: synchronized, wait/notify/notifyAll, and volatile. The specification also provides a “memory model” that describes how the programmer can share data reliably between threads. Using these low-level features presents no small challenge, and is error prone.
Contrary to popular expectation, code written this way is often not faster than code created using the high level java.util.concurrent libraries. Despite this, there are two good reasons for understanding these and the underlying memory model. One is that it's quite common to have code written in this way that must be maintained, and such maintenance is impractical without an understanding of these features. Second, when writing code using the higher level libraries, the memory model, or more specifically, the “happens-before” relationship still guides how and when we should use these libraries.
This workshop presents these features in a way designed to allow you to perform maintenance, and write new code without being dangerous.
One of the more troublesome parts of creating good quality software is how we handle problems that interrupt the “happy path”. Exacerbating this problem, most of our design methodologies don't seem to offer much advice on the topic. In this session we'll investigate some general categories of failure and consider the macro behaviors that are appropriate to each and where–in terms of the call stack–those actions should be taken.
The discussion will compare the strengths and weaknesses of the most common ways of representing failure: sentinel values, exceptions (both Java's checked variant, and the more common unchecked approach), and the functional technique based on monads. Along the way, we'll consider the abstraction of error, the consequences of getting this design aspect wrong, and how the design of our APIs might make errors less common in the first place.