Why use _ prefix in JS when encapsulation can be achieved with scoped variables? - naming-conventions

I've just started a new job with rigorous in-house code standards. One of them, that I've seen plenty of times before in Javascript, is using _ as a prefix for the properties of an object that should not be accessed from outside that object.
My de facto personal coding standard tends to use a lot of functions and primitives, and when I exposes an object with methods these will usually be syntactic sugar, aliases for generic plugins to be repeatedly used: I tend to keep everything very tightly-scoped such that each function has a distinct purpose and is only given the data it needs. As a result, within any given scope, only the bare minimum is exposed, and everything is private by default.
The underscore-prefixed-properties convention is closely related (as I see it) to two other big conventions here, one being Hungarian notation, the other being the use of a monolithic god object that everything must be added to. So I can see how, culturally, the _ fits in well since
Everything must be exposed as a property (of a property, etc) within a global object
Seeing as any piece of code can thus see any other piece of code, a meta-coded system is used to try and help programmers not trample each-other's code by mistake
The coding standards are held on a pedestal, but they were received, such that nobody here can adequately explain what they're for, or why. My cynical perspective is that in the earliest instance, developers either didn't see fit to implement generic, decoupled code, or didn't know how to encapsulate in JS, and later on the Hungarian notation was developed to allow some consistency while effectively having to compensate with human cognitive effort for the inherently dangerous code architecture.
Can anybody give me a less cynical explanation of the value of exposed but _-prefixed properties?

A reason to use _ prefixes (also sometimes m_) is to make a distinction between instance variables on the one side and local variables and parameters on the other side, so they cannot be mixed up, especially if they have the same name.
Another reason would be to make a distinction between non-public instance variables and publicly visible properties, which are backed-up by the instance variables and should thus have the same name. In languages like VB.NET this distinction cannot be made via casing, so a prefix is added.

Related

Encapsulation: allow accessing of fields of any other than the current receiver object

Some object oriented languages (e.g. Smalltalk) do not allow accessing of
fields of any other than the current receiver object. For example:
expressions like this.good, or this.like:=false would be legal, but expressions like x.like or this.like.good are illegal.
What I don't understand is: why??
What is the rationale for such a restriction?
This is one the the core ideas of OOP called encapsulation. No one except from the object itself knows about it's internal state.
This provides better isolation, as internal state can be changed over the time an if you are accessing it directly - you're screwed. Also if someone can mess with your object's state directly you never know if something will change during a runtime when you don't expect it.
In general it's not hard to define accessors, an in the end you end up with: x like, x like: false in smalltalk and x.like(), x.setLike(false) in C-like languages. Ruby and Scala allow you to define methods with spaces and invoke them without parenthesis so they look just like field access: x.like, x.like = false. You don't have a big overhead if you are forced to write accessors, but if you allow programmers to do everything they want with objects state then you get a chaos in your code, and this is actually a big problem.
To understand all bad things that can happen if you don't use it some time is required. And when you begin to develop, you don't understand what can happen if you keep fields public. That's why C++ is a bad language to start with, as for beginners it's easier to deal with direct field access.
Also if you think about accessing fields directly, then whole idea of OOP is broken. Because you can use any data you have as in procedural languages, and classes then just play a role of grouping functions of defining data structures.
You can read more about encapsulation on wikipedia.
Also there is a very interesting post about What is Object Oriented Programming: A Critical Approach
Adele Goldberg put it rather graphically with the aphorism "ask, don't touch".

Objective-C class naming convention vs Uncle Bob

In Chapter 2: Meaningful Names Uncle Bob writes:
Don't Add Gratuitous Context
In an imaginary application called "Gas Station Deluxe," it is bad idea to prefix every class with GDS. Frankly, you are working against your tools. You type G and the press completion key and are rewarded with a mile-long list of every class in your system
Actually that what I discovered during my first days with Objective-C a bit more than one year ago. After Java it was quite disappointing but I thought I'm only one who annoyed about that :)
I understand, that "Clean Code" book refers to Java most of the time and Java has namespaces (packages) unlike Objective-C.
Do you use 2-3 letters prefix in your classes if you're building an app, not a library?
What do you think, is it bad language design, language "feature" or Uncle Bob wasn't right here?
Perhaps the key word here is gratuitous. In Objective-C, prefixes serve the important purpose of reducing the chance of name collisions. In other languages like Java and C++, the existence of support for namespaces makes the use of prefixes gratuitous (and a violation of the oft-cited DRY principle). In Objective-C, however, prefixes are meaningful, useful, and not gratuitous.
I was tempted to close this question, but I don't think I've seen a similar one asked before and it's a valid question. Here are my rather disorganized thoughts on the matter.
Many languages have a feature called namespaces, where the "fully qualified" class name is prefixed by a hierarchical series of names. For example, the String class in Java is, properly, java.lang.String, and a custom class is properly com.whatever.foobar.MyClass.
Unfortunately, namespaces have never been added to Objective-C, which means that Objective-C symbols (class names, protocol names, and a few various other types) cannot be placed in a namespace even when using Objective-C++ (which has a namespace feature for functions, constants, structures, etc.)
The only solution to prevent symbol collisions in shared code, then, is to use some form of name mangling to make your symbol names unique. In Objective-C, the convention is to use a prefix of two characters (sometimes the number varies) for all your classes.
This Uncle Bob fellow is a twit for telling you not to do this, because while you'll end up with a program that doesn't compile, you'll lose any benefit of namespaces that prefixes still offer. Does your app use plugins? You need to prefix. Does your app have a public API? You need to prefix.
In theory, code within a single application that never touches the outside world can do without prefixes, but screw it--keep coding cleanly, and add a prefix even there. It'll save you grief later.
Personally I almost never use prefixes. The only exceptions are classes that are somehow connected to each other or they all should be present.
An example:
Some client app for chat. Let's call that chat an ExampleChat.
Then I'd use ECMessage, ECUser, ECRoom, etc. to easily see which classes should there be.
Or if I make some custom cells for UITableView I'd use prefixes to keep them all close to each other and not struggle with searching them in a "mile-long list". Example:
ECTextMessageCell, ECSoundMessageCell, ECUploadMessageCell, ECJoinOrLeaveMessageCell, etc.
That's my personal opinion, which can not be the best. But it's still easiest for me.
Hope it helps
Well if you do not have Namespaces, name conflicts are likely to occur. You can see that in a lot of C libraries that they are using some kind of prefix. So I guess there are good reasons to have those prefixes and other reasons not to use it. But what should be the big problem to modify the completion to either just ignore the prefix of typing three letters instead of just one.
So in the end it seems to me a matter of taste. I guess it would be more important to have good structures classes with prefixes instead of a mess of classes without prefix....
It has nothing to do with bad language design IMHO. There was a time where software was not everywhere and why should one waste extra effort on namespaces? And still as we can see even nowadays languages without namespaces are used.....
I would say, that the world is not black or white. I do programming in java, with packages and yes, it is annoying to have a prefix in each class, as well as it is annoying and arguable to start interfaces with I (just like .Net used to do it).
Sometimes it does annoying me in objective-c however it has some legitimacy if you do not have packages in your language, since you can 'build' artificial groups of classes like 'NS', 'UI', 'MK' and so on in objc and cocoa.
Beyond avoiding collisions, one of the benefits that name prefixes gives is that you're immediately aware of what type you're really dealing with. Suppose you had the following code:
Color c = ...;
MultiValueMap m = ...;
From a cursory glance at the code and depending on what libraries you've used, those types could be from a number of different sources. You may have to lookup which include/import statement was made to understand what the type can do (e.g. you want to modify it but it's missing a method that you're sure is there).
In the iOS world, you would immediately know whether it's a UIColor vs. a CGColor and gain immediate context.
In the past at WWDC, Apple would host a session where they explained Cocoa/Objective-C coding conventions. I believe they mention this aspect of name prefixes so you might want to find one of the recordings that are made available. Other C developers (e.g. Linux kernel developers) also do not seem to think highly of C++ namespaces (among other C++ features) for various reasons.

Who do we protect our classes from?

I'm currently learning C# and I was wondering, what is the point of declaring classes / methods private? Who are we hiding / limiting access to these classes.
Because if someone was editing the source they could just change the tag from private to public. I'm not sure how a user will be able to access these methods and what problems it would cause.
tldr; What's the point of access modifiers.
Member visibility, as this feature is often called, is not a security feature. It is a convenience for the programmer, designed to help limit cross-class dependencies. By declaring a member private, you prevent other code from accessing it directly. This has two advantages:
if you find that a member variable gets manipulated in a way you did not intend, the amount of code you have to check is significantly smaller when the variable is private
you can change the inner workings of a class (everything that is declared private) without breaking the interface (everything declared public)
Member visibility is probably the most important language feature in realizing encapsulation, one of the core principles of object-oriented programming.
This is a basic OO concept - encapsulation and has mostly nothing to do with security per se. To quote from Wikipedia (emphasis mine):
Hiding the internals of the object protects its integrity by
preventing users from setting the internal data of the component into
an invalid or inconsistent state. A benefit of encapsulation is that
it can reduce system complexity, and thus increases robustness, by
allowing the developer to limit the interdependencies between software
components.
It keeps your code tidy. You separate your code into a public interface, and private internals.
That way, you can change your internals without fear of breaking code that depends on your class. You can also safely assume that no other code has modified your internal state while you weren't looking.
Access modifiers are used for encapsulation: they allow you to arrange
your code in packages and classes, and have only an "official" public
interface visible to the outside, while hiding the implementation
details (which you want to do, so that you can later change it without
telling anyone).
This is especially (only?) important, when you release code as a
library for other programmers to use. Even if the code is only used in
your own program, it helps to structure larger programs into several
packages. - Thilo
You can write code in 2 ways: designed for extension and designed for usage as it is.
You may or may not have access to source code.
Access modifiers are most important in code designed for extension where the client programmer (the programmer which extends and uses your code) needs to have access only to the API you expose - basically, to the interface you offer by using these access modifiers.
Access modifiers are used for encapsulation, so that the internals of an object/class are hidden from outside users or classes. This prevents users from accidentally or intentionally breaking an object/class by modifying its instance variables and/or functions.
Encapsulation is an important part of the Open/closed principle, which refers to a class being "open for extension" but "closed for modification". This principle allows the extension of classes without fear that the API of an class may change in the future.

Is a function an example of encapsulation?

By putting functionality into a function, does that alone constitute an example of encapsulation or do you need to use objects to have encapsulation?
I'm trying to understand the concept of encapsulation. What I thought was if I go from something like this:
n = n + 1
which is executed out in the wild as part of a big body of code and then I take that, and put it in a function such as this one, then I have encapsulated that addition logic in a method:
addOne(n)
n = n + 1
return n
Or is it more the case that it is only encapsulation if I am hiding the details of addOne from the outside world - like if it is an object method and I use an access modifier of private/protected?
I will be the first to disagree with what seems to be the answer trend. Yes, a function encapsulates some amount of implementation. You don't need an object (which I think you use to mean a class).
See Meyers too.
Perhaps you are confusing abstraction with encapsulation, which is understood in the broader context of object orientation.
Encapsulation properly includes all three of the following:
Abstraction
Implementation Hiding
Division of Responsibility
Abstraction is only one component of encapsulation. In your example you have abstracted the adding functionality from the main body of code in which it once resided. You do this by identifying some commonality in the code - recognizing a concept (addition) over a specific case (adding the number one to the variable n). Because of this ability, abstraction makes an encapsulated component - a method or an object - reusable.
Equally important to the notion of encapsulation is the idea of implementation hiding. This is why encapsulation is discussed in the arena of object orientation. Implementation hiding protects an object from its users and vice versa. In OO, you do this by presenting an interface of public methods to the users of your object, while the implementation of the object takes place inside private methods.
This serves two benefits. First, by limiting access to your object, you avoid a situation where users of the object can leave the object in an invalid state. Second, from the user's perspective, when they use your object they are only loosely coupled to it - if you change your implementation later on, they are not impacted.
Finally, division of responsility - in the broader context of an OO design - is something that must be considered to address encapsulation properly. It's no use encapsulating a random collection of functions - responsibility needs to be cleanly and logically defined so that there is as little overlap or ambiguity as possible. For example, if we have a Toilet object we will want to wall off its domain of responsibilities from our Kitchen object.
In a limited sense, though, you are correct that a function, let's say, 'modularizes' some functionality by abstracting it. But, as I've said, 'encapsulation' as a term is understood in the broader context of object orientation to apply to a form of modularization that meets the three criteria listed above.
Sure it is.
For example, a method that operates only on its parameters would be considered "better encapsulated" than a method that operates on global static data.
Encapsulation has been around long before OOP :)
A method is no more an example of encapsulation than a car is an example of good driving. Encapsulation isn't about the synax, it is a logical design issue. Both objects and methods can exhibit good and bad encapsulation.
The simplest way to think about it is whether the code hides/abstracts the details from other parts of the code that don't have a need to know/care about the implementation.
Going back to the car example:
Automatic transmission offers good encapsulation: As a driver you care about forward/back and speed.
Manual Transmission is bad encapsulation: From the driver's perspective the specific gear required for low/high speeds is generally irrelevant to the intent of the driver.
No, objects aren't required for encapsulation. In the very broadest sense, "encapsulation" just means "hiding the details from view" and in that regard a method is encapsulating its implementation details.
That doesn't really mean you can go out and say your code is well-designed just because you divided it up into methods, though. A program consisting of 500 public methods isn't much better than that same program implemented in one 1000-line method.
In building a program, regardless of whether you're using object oriented techniques or not, you need to think about encapsulation at many different places: hiding the implementation details of a method, hiding data from code that doesn't need to know about it, simplifying interfaces to modules, etc.
Update: To answer your updated question, both "putting code in a method" and "using an access modifier" are different ways of encapsulating logic, but each one acts at a different level.
Putting code in a method hides the individual lines of code that make up that method so that callers don't need to care about what those lines are; they only worry about the signature of the method.
Flagging a method on a class as (say) "private" hides that method so that a consumer of the class doesn't need to worry about it; they only worry about the public methods (or properties) of your class.
The abstract concept of encapsulation means that you hide implementation details. Object-orientation is but one example of the use of ecnapsulation. Another example is the language called module-2 that uses (or used) implementation modules and definition modules. The definition modules hid the actual implementation and therefore provided encapsulation.
Encapsulation is used when you can consider something a black box. Objects are a black box. You know the methods they provide, but not how they are implemented.
[EDIT]
As for the example in the updated question: it depends on how narrow or broad you define encapsulation. Your AddOne example does not hide anything I believe. It would be information hiding/encapsulation if your variable would be an array index and you would call your method moveNext and maybe have another function setValue and getValue. This would allow people (together maybe with some other functions) to navigate your structure and setting and getting variables with them being aware of you using an array. If you programming language would support other or richer concepts you could change the implementation of moveNext, setValue and getValue with changing the meaning and the interface. To me that is encapsulation.
It's a component-level thing
Check this out:
In computer science, Encapsulation is the hiding of the internal mechanisms and data structures of a software component behind a defined interface, in such a way that users of the component (other pieces of software) only need to know what the component does, and cannot make themselves dependent on the details of how it does it. The purpose is to achieve potential for change: the internal mechanisms of the component can be improved without impact on other components, or the component can be replaced with a different one that supports the same public interface.
(I don't quite understand your question, let me know if that link doesn't cover your doubts)
Let's simplify this somewhat with an analogy: you turn the key of your car and it starts up. You know that there's more to it than just the key, but you don't have to know what is going on in there. To you, key turn = motor start. The interface of the key (that is, e.g., the function call) hides the implementation of the starter motor spinning the engine, etc... (the implementation). That's encapsulation. You're spared from having to know what's going on under the hood, and you're happy for it.
If you created an artificial hand, say, to turn the key for you, that's not encapsulation. You're turning the key with additional middleman cruft without hiding anything. That's what your example reminds me of - it's not encapsulating implementation details, even though both are accomplished through function calls. In this example, anyone picking up your code will not thank you for it. They will, in fact, be more likely to club you with your artificial hand.
Any method you can think of to hide information (classes, functions, dynamic libraries, macros) can be used for encapsulation.
Encapsulation is a process in which attributes(data member) and behavior(member function) of a objects in combined together as a single entity refer as class.
The Reference Model of Open Distributed Processing - written by the International Organisation for Standardization - defines the following concepts:
Entity: Any concrete or abstract thing of interest.
Object: A model of an entity. An object is characterised by its behaviour and, dually, by its state.
Behaviour (of an object): A collection of actions with a set of constraints on when they may occur.
Interface: An abstraction of the behaviour of an object that consists of a subset of the interactions of that object together with a set of constraints on when they may occur.
Encapsulation: the property that the information contained in an object is accessible only through interactions at the interfaces supported by the object.
These, you will appreciate, are quite broad. Let us see, however, whether putting functionality within a function can logically be considered to constitute towards encapsulation in these terms.
Firstly, a function is clearly a model of a, 'Thing of interest,' in that it represents an algorithm you (presumably) desire executed and that algorithm pertains to some problem you are trying to solve (and thus is a model of it).
Does a function have behaviour? It certainly does: it contains a collection of actions (which could be any number of executable statements) that are executed under the constraint that the function must be called from somewhere before it can execute. A function may not spontaneously be called at any time, without causal factor. Sounds like legalese? You betcha. But let's plough on, nonetheless.
Does a function have an interface? It certainly does: it has a name and a collection of formal parameters, which in turn map to the executable statements contained in the function in that, once a function is called, the name and parameter list are understood to uniquely identify the collection of executable statements to be run without the calling party's specifying those actual statements.
Does a function have the property that the information contained in the function is accessible only through interactions at the interfaces supported by the object? Hmm, well, it can.
As some information is accessible via its interface, some information must be hidden and inaccessible within the function. (The property such information exhibits is called information hiding, which Parnas defined by arguing that modules should be designed to hide both difficult decisions and decisions that are likely to change.) So what information is hidden within a function?
To see this, we should first consider scale. It's easy to claim that, for example, Java classes can be encapsulated within a package: some of the classes will be public (and hence be the package's interface) and some will be package-private (and hence information-hidden within the package). In encapsulation theory, the classes form nodes and the packages form encapsulated regions, with the entirety forming an encapsulated graph; the graph of classes and packages is called the third graph.
It's also easy to claim that functions (or methods) themselves are encapsulated within classes. Again, some functions will be public (and hence be part of the class's interface) and some will be private (and hence information-hidden within the class). The graph of functions and classes is called the second graph.
Now we come to functions. If functions are to be a means of encapsulation themselves they they should contain some information public to other functions and some information that's information-hidden within the function. What could this information be?
One candidate is given to us by McCabe. In his landmark paper on cyclomatic complexity, Thomas McCabe describes source code where, 'Each node in the graph corresponds to a block of code in the program where the flow is sequential and the arcs correspond to branches taken in the program.'
Let us take the McCabian block of sequential execution as the unit of information that may be encapsulated within a function. As the first block within the function is always the first and only guaranteed block to be executed, we can consider the first block to be public, in that it may be called by other functions. All the other blocks within the function, however, cannot be called by other functions (except in languages that allow jumping into functions mid-flow) and so these blocks may be considered information-hidden within the function.
Taking these (perhaps slightly tenuous) definitions, then we may say yes: putting functionality within a function does constitute to encapsulation. The encapsulation of blocks within functions is the first graph.
There is a caveate, however. Would you consider a package whose every class was public to be encapsulated? According to the definitions above, it does pass the test, as you can say that the interface to the package (i.e., all the public classes) do indeed offer a subset of the package's behaviour to other packages. But the subset in this case is the entire package's behaviour, as no classes are information-hidden. So despite regorously satisfying the above definitions, we feel that it does not satisfy the spirit of the definitions, as surely something must be information-hidden for true encapsulation to be claimed.
The same is true for the exampe you give. We can certainly consider n = n + 1 to be a single McCabian block, as it (and the return statement) are a single, sequential flow of executions. But the function into which you put this thus contains only one block, and that block is the only public block of the function, and therefore there are no information-hidden blocks within your proposed function. So it may satisfy the definition of encapsulation, but I would say that it does not satisfy the spirit.
All this, of course, is academic unless you can prove a benefit such encapsulation.
There are two forces that motivate encapsulation: the semantic and the logical.
Semantic encapsulation merely means encapsulation based on the meaning of the nodes (to use the general term) encapsulated. So if I tell you that I have two packages, one called, 'animal,' and one called 'mineral,' and then give you three classes Dog, Cat and Goat and ask into which packages these classes should be encapsulated, then, given no other information, you would be perfectly right to claim that the semantics of the system would suggest that the three classes be encapsulated within the, 'animal,' package, rather than the, 'mineral.'
The other motivation for encapsulation, however, is logic.
The configuration of a system is the precise and exhaustive identification of each node of the system and the encapsulated region in which it resides; a particular configuration of a Java system is - at the third graph - to identify all the classes of the system and specify the package in which each class resides.
To logically encapsulate a system means to identify some mathematical property of the system that depends on its configuration and then to configure that system so that the property is mathematically minimised.
Encapsulation theory proposes that all encapsulated graphs express a maximum potential number of edges (MPE). In a Java system of classes and packages, for example, the MPE is the maximum potential number of source code dependencies that can exist between all the classes of that system. Two classes within the same package cannot be information-hidden from one another and so both may potentially form depdencies on one another. Two package-private classes in separate packages, however, may not form dependencies on one another.
Encapsulation theory tells us how many packages we should have for a given number of classes so that the MPE is minimised. This can be useful because the weak form of the Principle of Burden states that the maximum potential burden of transforming a collection of entities is a function of the maximum potential number of entities transformed - in other words, the more potential source code dependencies you have between your classes, the greater the potential cost of doing any particular update. Minimising the MPE thus minimises the maximum potential cost of updates.
Given n classes and a requirement of p public classes per package, encapsulation theory shows that the number of packages, r, we should have to minimise the MPE is given by the equation: r = sqrt(n/p).
This also applies to the number of functions you should have, given the total number, n, of McCabian blocks in your system. Functions always have just one public block, as we mentioned above, and so the equation for the number of functions, r, to have in your system simplifies to: r = sqrt(n).
Admittedly, few considered the total number of blocks in their system when practicing encapsulation, but it's readily done at the class/package level. And besides, minimising MPE is almost entirely entuitive: it's done by minimising the number of public classes and trying to uniformly distribute classes over packages (or at least avoid have most packages with, say, 30 classes, and one monster pacakge with 500 classes, in which case the internal MPE of the latter can easily overwhelm the MPE of all the others).
Encapsulation thus involves striking a balance between the semantic and the logical.
All great fun.
in strict object-oriented terminology, one might be tempted to say no, a "mere" function is not sufficiently powerful to be called encapsulation...but in the real world the obvious answer is "yes, a function encapsulates some code".
for the OO purists who bristle at this blasphemy, consider a static anonymous class with no state and a single method; if the AddOne() function is not encapsulation, then neither is this class!
and just to be pedantic, encapsulation is a form of abstraction, not vice-versa. ;-)
It's not normally very meaningful to speak of encapsulation without reference to properties rather than solely methods -- you can put access controls on methods, certainly, but it's difficult to see how that's going to be other than nonsensical without any data scoped to the encapsulated method. Probably you could make some argument validating it, but I suspect it would be tortuous.
So no, you're most likely not using encapsulation just because you put a method in a class rather than having it as a global function.

Dealing with "global" data structures in an object-oriented world

This is a question with many answers - I am interested in knowing what others consider to be "best practice".
Consider the following situation: you have an object-oriented program that contains one or more data structures that are needed by many different classes. How do you make these data structures accessible?
You can explicitly pass references around, for example, in the constructors. This is the "proper" solution, but it means duplicating parameters and instance variables all over the program. This makes changes or additions to the global data difficult.
You can put all of the data structures inside of a single object, and pass around references to this object. This can either be an object created just for this purpose, or it could be the "main" object of your program. This simplifies the problems of (1), but the data structures may or may not have anything to do with one another, and collecting them together in a single object is pretty arbitrary.
You can make the data structures "static". This lets you reference them directly from other classes, without having to pass around references. This entirely avoids the disadvantages of (1), but is clearly not OO. This also means that there can only ever be a single instance of the program.
When there are a lot of data structures, all required by a lot of classes, I tend to use (2). This is a compromise between OO-purity and practicality. What do other folks do? (For what it's worth, I mostly come from the Java world, but this discussion is applicable to any OO language.)
Global data isn't as bad as many OO purists claim!
After all, when implementing OO classes you've usually using an API to your OS. What the heck is this if it isn't a huge pile of global data and services!
If you use some global stuff in your program, you're merely extending this huge environment your class implementation can already see of the OS with a bit of data that is domain specific to your app.
Passing pointers/references everywhere is often taught in OO courses and books, academically it sounds nice. Pragmatically, it is often the thing to do, but it is misguided to follow this rule blindly and absolutely. For a decent sized program, you can end up with a pile of references being passed all over the place and it can result in completely unnecessary drudgery work.
Globally accessible services/data providers (abstracted away behind a nice interface obviously) are pretty much a must in a decent sized app.
I must really really discourage you from using option 3 - making the data static. I've worked on several projects where the early developers made some core data static, only to later realise they did need to run two copies of the program - and incurred a huge amount of work making the data non-static and carefully putting in references into everything.
So in my experience, if you do 3), you will eventually end up doing 1) at twice the cost.
Go for 1, and be fine-grained about what data structures you reference from each object. Don't use "context objects", just pass in precisely the data needed. Yes, it makes the code more complicated, but on the plus side, it makes it clearer - the fact that a FwurzleDigestionListener is holding a reference to both a Fwurzle and a DigestionTract immediately gives the reader an idea about its purpose.
And by definition, if the data format changes, so will the classes that operate on it, so you have to change them anyway.
You might want to think about altering the requirement that lots of objects need to know about the same data structures. One reason there does not seem to be a clean OO way of sharing data is that sharing data is not very object-oriented.
You will need to look at the specifics of your application but the general idea is to have one object responsible for the shared data which provides services to the other objects based on the data encapsulated in it. However these services should not involve giving other objects the data structures - merely giving other objects the pieces of information they need to meet their responsibilites and performing mutations on the data structures internally.
I tend to use 3) and be very careful about the synchronisation and locking across threads. I agree it is less OO, but then you confess to having global data, which is very un-OO in the first place.
Don't get too hung up on whether you are sticking purely to one programming methodology or another, find a solution which fits your problem. I think there are perfectly valid contexts for singletons (Logging for instance).
I use a combination of having one global object and passing interfaces in via constructors.
From the one main global object (usually named after what your program is called or does) you can start up other globals (maybe that have their own threads). This lets you control the setting up of program objects in the main objects constructor and tearing them down again in the right order when the application stops in this main objects destructor. Using static classes directly makes it tricky to initialize/uninitialize any resources these classes use in a controlled manner. This main global object also has properties for getting at the interfaces of different sub-systems of your application that various objects may want to get hold of to do their work.
I also pass references to relevant data-structures into constructors of some objects where I feel it is useful to isolate those objects from the rest of the world within the program when they only need to be concerned with a small part of it.
Whether an object grabs the global object and navigates its properties to get the interfaces it wants or gets passed the interfaces it uses via its constructor is a matter of taste and intuition. Any object you're implementing that you think might be reused in some other project should definately be passed data structures it should use via its constructor. Objects that grab the global object should be more to do with the infrastructure of your application.
Objects that receive interfaces they use via the constructor are probably easier to unit-test because you can feed them a mock interface, and tickle their methods to make sure they return the right arguments or interact with mock interfaces correctly. To test objects that access the main global object, you have to mock up the main global object so that when they request interfaces (I often call these services) from it they get appropriate mock objects and can be tested against them.
I prefer using the singleton pattern as described in the GoF book for these situations. A singleton is not the same as either of the three options described in the question. The constructor is private (or protected) so that it cannot be used just anywhere. You use a get() function (or whatever you prefer to call it) to obtain an instance. However, the architecture of the singleton class guarantees that each call to get() returns the same instance.
We should take care not to confuse Object Oriented Design with Object Oriented Implementation. Al too often, the term OO Design is used to judge an implementation, just as, imho, it is here.
Design
If in your design you see a lot of objects having a reference to exactly the same object, that means a lot of arrows. The designer should feel an itch here. He should verify whether this object is just commonly used, or if it is really a utility (e.g. a COM factory, a registry of some kind, ...).
From the project's requirements, he can see if it really needs to be a singleton (e.g. 'The Internet'), or if the object is shared because it's too general or too expensive or whatsoever.
Implementation
When you are asked to implement an OO Design in an OO language, you face a lot of decisions, like the one you mentioned: how should I implement all the arrows to the oft used object in the design?
That's the point where questions are addressed about 'static member', 'global variable' , 'god class' and 'a-lot-of-function-arguments'.
The Design phase should have clarified if the object needs to be a singleton or not. The implementation phase will decide on how this singleness will be represented in the program.
Option 3) while not purist OO, tends to be the most reasonable solution. But I would not make your class a singleton; and use some other object as a static 'dictionary' to manage those shared resources.
I don't like any of your proposed solutions:
You are passing around a bunch of "context" objects - the things that use them don't specify what fields or pieces of data they are really interested in
See here for a description of the God Object pattern. This is the worst of all worlds
Simply do not ever use Singleton objects for anything. You seem to have identified a few of the potential problems yourself